1. 程式人生 > >CF875D High Cry 思維+倍增

CF875D High Cry 思維+倍增

題意:給你一個長度為 n的數列 {ai},求滿足 區間或 >區間最大值 的區間個數。

這個題卡了我差不多大半天。

後面還是看了題解才意識到怎麼做。看來還是我太菜了。。(╯0╰)

其實這個題最主要的是能夠這樣考慮:每個點作為最大值能覆蓋的區間。

也就是保證對於L[i] to R[i],a[i] 是區間中最大的那個。

 這樣的話我們就已經保證好了區間最大值是什麼。

然後我們列舉兩個區間中短的那個(不然會T掉,親測),另一邊倍增一下(因為 或的和 是單調遞增的),就可以記錄答案了。

我的話是記錄的不滿足條件的,也就是 :

(以左區間較短為例)假設列舉到以ai為最大值,左區間列舉到j,倍增後的位置是p,那麼不符合的答案就是p-j+1。

最後再減掉就可以了。。

#include <stdio.h>
#include <string.h>
inline int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

#define LL long long
#define
rep(i,st,ed) for (register int i=st;i<=ed;++i) #define drp(i,st,ed) for (register int i=st;i>=ed;--i) const int N=400005; int stack[N],a[N], L[N],R[N]; LL jj[N][21],gg[N][21]; int main(void) { int n=read(); rep(i,1,n) gg[i][0]=jj[i][0]=a[i]=read(); rep(j,1,18) { rep(i,1,n) jj[i][j]=jj[i][j-1
]|jj[i+(1<<(j-1))][j-1]; drp(i,n,1) { gg[i][j]=gg[i][j-1]; if (i>=(1<<(j-1))) gg[i][j]|=gg[i-(1<<(j-1))][j-1]; } } for (register int i=1,top=0;i<=n;++i) { for (;top&&a[stack[top]]<=a[i];) top--; L[i]=stack[top]+1; stack[++top]=i; } stack[0]=n+1; for (register int i=n,top=0;i>=1;--i) { for (;top&&a[stack[top]]<a[i];) top--; R[i]=stack[top]-1; stack[++top]=i; } LL ans=0; rep(i,1,n) if (i-L[i]<=R[i]-i) for (register int j=L[i];j<=i;++j) { LL ff=a[j]; int now=j; drp(k,18,0) { if ((now+(1<<k)<=R[i])&&((ff|jj[now+1][k])<=a[i])) { ff|=jj[now+1][k]; now+=(1<<k); } } if (now>=i&&now<=R[i]) ans+=now-i+1; } else for (register int j=i;j<=R[i];++j) { LL ff=a[j]; int now=j; drp(k,18,0) if ((now-(1<<k)>=L[i])&&((ff|gg[now-1][k])<=a[i])) { ff|=gg[now-1][k]; now-=(1<<k); } if (now<=i&&now>=L[i]) ans+=i-now+1; } printf("%lld\n", 1LL*n*(n+1LL)/2LL-ans); return 0; }