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;
}

 

posted @ 2018-10-21 22:18  薄荷凉了夏  阅读(267)  评论(0编辑  收藏  举报