BZOJ3956 Count(單調棧 線段樹)

2021-07-17 00:04:25 字數 1592 閱讀 7223

傳送門

感覺這道題蘊含了一些十分巧妙的性質。

可以發現好點對實際上很少,最多不超過2n個。當乙個點作為點對中的較小點的時候,最多只能和它左邊第乙個大於等於它的點以及它右邊第乙個大於等於它的點配對。

維護乙個單調遞減的棧就可以求出所有的好點對。

還可以發現對於一段詢問的區間,找出區間中最大的點的位置,那麼區間中的所有好點對都不會跨越這個點。那麼就計算左端點在最大點左邊的個數,右端點在最大點右邊的個數就可以了。時間複雜度o(

nlog

n),用o(

1)rmq實際上是可以做到o(

n)的。

#include

#include

#include

using

namespace

std;

const

int n=1e6+5;

const

int t=n*4;

int n,m,type,top,cnt,l,r,loc,ans;

int a[n],ls[n],rs[n],stack[n],maxn[n*4];

struct hpcp[n*2];

inline

int read()

inline

void out(int x)

int now=0; int print[10];

while (x>0)

for (int i=now;i>=1;--i) putchar(print[i]+'0');

putchar('\n');

}inline

void pre()

stack[++top]=i;

}while (top>1) cp[++cnt].l=stack[top-1],cp[cnt].r=stack[top],top--;

for (int i=1;i<=cnt;++i) ls[cp[i].l]++;

for (int i=1;i<=cnt;++i) rs[cp[i].r]++;

for (int i=2;i<=n;++i) ls[i]+=ls[i-1];

for (int i=2;i<=n;++i) rs[i]+=rs[i-1];

}inline

void update(int now)

inline

void build(int now,int l,int r)

build(now<<1,l,mid);

build(now<<1|1,mid+1,r);

update(now);

}inline

int query(int now,int l,int r,int lrange,int rrange)

if (mid+1

<=rrange)

return ans;

}int main()

else l=ll,r=rr;

loc=query(1,1,n,l,r);

ans=ls[loc-1]-ls[l-1]+rs[r]-rs[loc];

out(ans);

}}

①多想想題目中隱藏的條件和性質,可以用奇怪的方法證明。

BZOJ 3956 Count 單調棧 ST表

題目傳送門 挺有思想的一題,但如果弄清楚了思路這題還是挺簡單的。首先我們可以發揮一下自己的腦洞,發現所有的好集對不可能相交。那麼我們可以刷兩遍單調棧,求出每個點作為區間左端點或右端點的次數。對於給定區間,我們先找出區間中最大值所在的位置,顯然所有的好集對最多以這個位置為左端點或右端點,不可能包含這個...

bzoj3956 Count 單調棧 st表

bzoj 非常巧妙的一道題 類似 hnoi影魔 每個點會給左右第乙個大於它的點對產生貢獻 可以用單調棧求出 這裡有點小細節,就是處理相等的點時,最左邊的點管左邊的貢獻,最右邊的點管最右邊的貢獻 然後對於每個點,求出了一對 x,y 那麼,對於詢問區間 l,r 答案就是有多少個 x,y 在區間 l,r ...

BZOJ3956 Count 主席樹 單調棧

3 2 0 2 1 2 1 11 303 m,n 3 10 5,ai 10 9 題解 影魔那道題的簡化版,依舊用單調棧記錄每個數左 右 邊第乙個比它大的數為ld i rd i 那麼合法的點對只有 i,i 1 和 ld i rd i 兩種。為了防止重複,我們記錄每個數左邊第乙個 它的數le i 當且僅...