题意:给你一个长度为 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 ", 1LL*n*(n+1LL)/2LL-ans); return 0; }