2020牛客暑期多校训练营(第五场)H Interval —— 主席树+线段树,位与,有丶东西

This way

题意:

定义 F ( l , r ) = A l & A l + 1 & . . . & A r F(l,r)=A_l\&A_{l+1}\&...\&A_r F(l,r)=Al&Al+1&...&Ar
S ( l , r ) S(l,r) S(l,r) F ( a , b ) ( l < = a < = b < = r ) F(a,b)(l<=a<=b<=r) F(a,b)(l<=a<=b<=r)的不同元素的个数。
每次给你l,r问你S(l,r)的值

题解:

如果是枚举每个位置,然后找每个位置左边有多少不同元素的个数,然后再去重的话,可以知道,对于任意一个Ai,最多只会变__builtin_popcount(Ai)次,也就是每次二进制下少掉一个1.那么这个我们就可以通过30次二分来找到每个点的变换位置。
但是我们肯定不能每次都从左到右for一遍,那么这里就用主席树记录每个点作为根的树的时候的答案的位置即可。类似区间不重复的数的个数的做法,每次二分完之后将那个位置的值+1,如果前面出现过,就要减掉。
最后注意主席树的空间,由于这里要进行很多次对主席树的操作,那么空间就要开的比较大,我就是wa在这里很多发。我测了一下大概要开到150倍的空间才能过去

#include
using namespace std;
const int N=1e5+5;
unordered_map<int,int>pre;
struct Chairman{
    int ls[N*300],rs[N*300],rt[N],sum[N*300],tot;
    void update(int l,int r,int root,int last,int p,int v){
        ls[root]=ls[last];
        rs[root]=rs[last];
        sum[root]=sum[last]+v;
        if(l==r)return ;
        int mid=l+r>>1;
        if(mid>=p)
            update(l,mid,ls[root]=++tot,ls[last],p,v);
        else
            update(mid+1,r,rs[root]=++tot,rs[last],p,v);
    }
    int query(int l,int r,int root,int ql,int qr){
        if(l>=ql&&r<=qr)
            return sum[root];
        int mid=l+r>>1;
        int ans=0;
        if(mid>=ql)
            ans=query(l,mid,ls[root],ql,qr);
        if(mid<qr)
            ans+=query(mid+1,r,rs[root],ql,qr);
        return ans;
    }
}cmt;
int a[N];
struct Segment{
    int b[N*4];
    void build(int l,int r,int root){
        if(l==r){
            b[root]=a[l];
            return ;
        }
        int mid=l+r>>1;
        build(l,mid,root<<1);
        build(mid+1,r,root<<1|1);
        b[root]=b[root<<1]&b[root<<1|1];
    }
    int query(int l,int r,int root,int ql,int qr){
        if(l>=ql&&r<=qr)
            return b[root];
        int mid=l+r>>1,ans=(1<<30)-1;
        if(mid>=ql)
            ans&=query(l,mid,root<<1,ql,qr);
        if(mid<qr)
            ans&=query(mid+1,r,root<<1|1,ql,qr);
        return ans;
    }
}sgt;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sgt.build(1,n,1);
    for(int i=1;i<=n;i++){
        if(pre[a[i]]){
            cmt.update(1,n,cmt.rt[i]=++cmt.tot,cmt.rt[i-1],pre[a[i]],-1);
            int now=++cmt.tot;
            cmt.update(1,n,now,cmt.rt[i],i,1);
            cmt.rt[i]=now;
        }
        else
            cmt.update(1,n,cmt.rt[i]=++cmt.tot,cmt.rt[i-1],i,1);
        pre[a[i]]=i;
        int v=a[i],p=i;
        for(int j=30;j;j--){
            int l=1,r=p-1,mid,ans=0;
            while(r>=l){
                mid=l+r>>1;
                if(sgt.query(1,n,1,mid,i)<v)
                    ans=mid,l=mid+1;
                else
                    r=mid-1;
            }
            if(!ans)break;
            v=sgt.query(1,n,1,ans,i);
            p=ans;
            int now;
            if(pre[v]){
                now=++cmt.tot;
                cmt.update(1,n,now,cmt.rt[i],pre[v],-1);
                cmt.rt[i]=now;
            }
            now=++cmt.tot;
            cmt.update(1,n,now,cmt.rt[i],p,1);
            cmt.rt[i]=now;
            pre[v]=p;
        }
    }
    int q,ans=0;
    scanf("%d",&q);
    while(q--){
        int l,r;
        scanf("%d%d",&l,&r);
        l^=ans,r^=ans;
        l=l%n+1,r=r%n+1;
        if(l>r)swap(l,r);
        printf("%d\n",ans=cmt.query(1,n,cmt.rt[r],l,r));
    }
    return 0;
}

你可能感兴趣的:(主席树,想法,线段树)