題目大意: 給出n
個數,定義一種好點對(i
,j)(
令i需要滿足如下兩個條件之一: 1.i
=j−1
2.對於∨k
∈(i,
j),均有a[
k]n(a[
i],a
[j])
詢問若干個區間中的好點對個數。
題解:
我們首先可以發現,這樣的好點對是不相交的(但會出現包含情況),既不可能出現兩個好點對(i
1,j1
),(i
2,j2
) ,使得j1
>i2
且j1,因為那樣的話這兩個好點對就會變成(i
1,i2
),(j
1,j2
) 。
那這樣的話我們可以預處理出來以每個點作為
i 和j(
i的好點對數量,顯然只用從這個點向前或向後掃到第乙個大於等於它的點,那這就可以正反掃兩遍,用單調棧維護一下就ok
,然後對這兩種情況分別做字首和sl
,sr 。
然後我們需要知道乙個區間中的最大值,這個用st
表o(n
log(
n)) 的預處理一下就可以o(
1)的查詢。
對於每個詢問區間[l
,r] ,我們先查出區間的最大值,有兩種情況:
1.如果這個最大值是
l 或
r中的乙個,那區間的好點對個數就是sl
r−sl
l−1 ;
2.如果是在區間中間,那就以這個最大值為界,將原區間分成[l
,max
x],[
maxx
,r] ,那這兩個區間就都變成了情況1。 ps
. 答案不會超過2n
code:
#include
#include
#include
#include
#include
#include
using namespace std;
#define n 300010
#define ll long long
int n,m,a[n],q[n<<1],l[n],r[n],st[n][20],w[n],ans,sl[n],sr[n];
inline int in()while (ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
if (!f) x=-x; returnx;}
inline void init()for (i=1; i<=n; i++) sr[i]=sr[i-1]+r[i];
top=1,q[top]=n;
for (i=n-1; i>=1; i--)for (i=1; i<=n; i++) sl[i]=sl[i-1]+l[i];
for (i=1; i<=n; i++) st[i][0]=i;
for (j=1; (1
<<(j-1))<=n; i++)
for (i=1,j=0; i<=n; i++)
}inline int query(int l,int r)
inline void work(int l,int r)
int main()return
0;}
BZOJ3956 Count(單調棧 線段樹)
傳送門 感覺這道題蘊含了一些十分巧妙的性質。可以發現好點對實際上很少,最多不超過2n個。當乙個點作為點對中的較小點的時候,最多只能和它左邊第乙個大於等於它的點以及它右邊第乙個大於等於它的點配對。維護乙個單調遞減的棧就可以求出所有的好點對。還可以發現對於一段詢問的區間,找出區間中最大的點的位置,那麼區...
BZOJ 3956 Count 單調棧 ST表
題目傳送門 挺有思想的一題,但如果弄清楚了思路這題還是挺簡單的。首先我們可以發揮一下自己的腦洞,發現所有的好集對不可能相交。那麼我們可以刷兩遍單調棧,求出每個點作為區間左端點或右端點的次數。對於給定區間,我們先找出區間中最大值所在的位置,顯然所有的好集對最多以這個位置為左端點或右端點,不可能包含這個...
bzoj3956 Count 單調棧 st表
bzoj 非常巧妙的一道題 類似 hnoi影魔 每個點會給左右第乙個大於它的點對產生貢獻 可以用單調棧求出 這裡有點小細節,就是處理相等的點時,最左邊的點管左邊的貢獻,最右邊的點管最右邊的貢獻 然後對於每個點,求出了一對 x,y 那麼,對於詢問區間 l,r 答案就是有多少個 x,y 在區間 l,r ...