[主席树] Codechef: Prefix XOR

题意

给你 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 second
using 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值