[主席树] Codechef: Prefix XOR

来源:互联网 发布:编程语言 知乎 编辑:程序博客网 时间:2024/05/19 02:39

题意

给你 n 个数,每个数的值是 ai ,定义数对 (i,j)上升的当且仅当:
aiai xor ai+1ai xor ai+1 xor ai+2ai xor ai+1 xor ai+2xor aj
Q 个询问,每个询问给出一个区间 [L,R] ,求其中有多少个子区间是上升的

题解

不错的题目。
首先要想到将限制条件简化。
如果我们能预处理出 Right[i],表示从 i 开始最多推到的位置,事情就变得简单了。
假设我们已经比较快的求得了 Right 数组,那么每次询问就相当于求:

i=LR(min(R,Right[i])i+1)=i=LRmin(R,Right[i])(L1+R1)(RL+1)/2

关键在 Ri=Lmin(R,Right[i]) 怎么求。其实很简单,只要得到大于 R 的所有在区间中的 Right[i] 的个数和总和,然后随便搞一下就行了。主席树即可。
最后的问题:如何求 Right 呢?
考虑能超过j这个位置的 Right[i] 需满足:
S(j1) xor S(i1)S(j) xor S(i1)
考虑 S(j)S(j1) 二进制从高到低第一位不同的,显然就是在这里限制了 S(i1) 的某一特定位置必须为特定值。
所以我们就从右往左扫,记下之前的限制 cfn[31][2],表示限制某一位不能是为 0/1 的最近的限制。
每次在之前的限制中得到最小的,求出right,然后加入限制。

#include<cstdio>#include<cstring>#include<algorithm>#define Fir first#define Sec secondusing namespace std;typedef long long LL;inline char gc(){    static char buf[100000],*p1=buf,*p2=buf;    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline int getint(){    char ch=gc(); int res=0,ff=1;    while(!('0'<=ch&&ch<='9')) ch=='-'?ff=-1:0, ch=gc();    while('0'<=ch&&ch<='9') res=(res<<3)+(res<<1)+ch-'0', ch=gc();    return res*ff; }inline void putll(LL x){ if(x/10) putll(x/10); putchar('0'+x%10);  }const int maxn=400005;struct node{    int L,R; LL sum; int cnt;    node* ch[2];    node(int t1=0,int t2=0,LL t3=0,int t4=0,node* t5=NULL){L=t1;R=t2;sum=t3;cnt=t4;ch[0]=ch[1]=t5;}    node(node* t1):L(t1->L),R(t1->R),sum(t1->sum),cnt(t1->cnt){ch[0]=t1->ch[0];ch[1]=t1->ch[1];}    void maintain(){ sum=ch[0]->sum+ch[1]->sum; cnt=ch[0]->cnt+ch[1]->cnt; }} nil,*null=&nil, *rt[maxn];typedef node* P_node; P_node Insert(P_node pre,int val){    P_node p=new node(pre);    if(p->L==val&&p->R==val){ p->sum+=val; p->cnt++; return p; }    if(val<=p->ch[0]->R) p->ch[0]=Insert(pre->ch[0],val);                    else p->ch[1]=Insert(pre->ch[1],val);    p->maintain(); return p;}typedef pair<LL,int> Pair;Pair merge(Pair x,Pair y){ return make_pair(x.Fir+y.Fir,x.Sec+y.Sec); }Pair Query(P_node p1,P_node p2,int L,int R){    if(R<p1->L||p1->R<L) return make_pair(0,0);    if(L<=p1->L&&p1->R<=R) return make_pair(p1->sum-p2->sum,p1->cnt-p2->cnt);    return merge(Query(p1->ch[0],p2->ch[0],L,R),Query(p1->ch[1],p2->ch[1],L,R));}P_node build(int L,int R){    P_node p=new node(L,R,0,0,null);    if(L==R) return p;    int mid=(L+R)>>1;    p->ch[0]=build(L,mid); p->ch[1]=build(mid+1,R);    p->maintain(); return p;}int n,Q,_t,s[maxn],Right[maxn],cfn[33][2];LL ans;int main(){    null->ch[0]=null->ch[1]=null;     n=getint(); _t=getint();    for(int i=1;i<=n;i++) s[i]=(getint()^s[i-1]);    memset(cfn,63,sizeof(cfn));    for(int i=n;i>=1;i--){        Right[i]=n; for(int j=0;j<=31;j++) Right[i]=min(Right[i],cfn[j][(s[i-1]>>j)&1]-1);        for(int k=31;k>=0;k--) if(((s[i]>>k)&1)!=((s[i-1]>>k)&1)){            cfn[k][(s[i]>>k)&1]=min(cfn[k][(s[i]>>k)&1],i);            break;        }    }    rt[0]=build(1,n); for(int i=1;i<=n;i++) rt[i]=Insert(rt[i-1],Right[i]);    Q=getint();    while(Q--){        int L=getint(),R=getint();        L=(L+ans*_t)%n+1; R=(R+ans*_t)%n+1; if(L>R) swap(L,R);        ans=Query(rt[R],rt[L-1],1,n).Fir; Pair t=Query(rt[R],rt[L-1],R,n);        ans=ans-t.Fir+(LL)t.Sec*R; ans=ans-(LL)(R-L+1)*(L+R-2)/2;        putll(ans); putchar('\n');    }    return 0;}