SnackDown 2017 Online Elimination Round #Prefix Xor -- 主席树

传送门
题目大意:

给你 n 个数,每个数的值是 ai ,定义数对 (i,j) 是上升的当且仅当

aiai xor ai+1ai xor ai+1 xor ai+2ai xor ai+1 xor ai+2xor aj

Q 个询问,每个询问给出两个整数 li,ri,求 liijri 的上升数对 (i,j) 的个数。

题解:

fi 表示最大的 j ,满足 (i,j) 为上升数对。
考虑第 k 个数,假设 x ak 二进制最大非 0 位。
那么只有当 ai xor ai+1ak x 位为 1 时满足 ai xor ai+1ak<ai xor ai+1ak1
枚举二进制每一位,更新 fi 。由于要强制在线,用主席树维护 fi

代码:
这里写代码片#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 400010
#define ll long long
#define M 31
char ss[30];
int Len;
inline void Print(ll x){
    if(!x){
        putchar(48);puts("");
        return;
    }
    for(Len=0;x;x/=10)ss[++Len]=x%10;
    while(Len)putchar(ss[Len--]+48);puts("");
}
struct Node{
    ll w,t;
    int s,l,r;
}c[N*80];
ll s2,s3,Ans;
int i,j,k,n,m,p,a[N],f[N],Q,Mi[2],b[N],Rt[N],Num,s1,x,y;
bool z;
inline int Min(int x,int y){
    return x<y?x:y;
}
inline int Get(int x){
    for(int i=M-1;i>=0;i--)
    if(x&(1<<i))return i;
    return -1;
}
inline void Insert(int& x,int y,int l,int r,int z,int p,int q){
    x=++Num;
    c[x]=c[y];
    c[x].s++;c[x].w+=q;c[x].t+=p;
    if(l==r)return;
    int Mid=l+r>>1;
    if(z<=Mid)Insert(c[x].l,c[y].l,l,Mid,z,p,q);else Insert(c[x].r,c[y].r,Mid+1,r,z,p,q);
}
inline void Query1(int x,int y,int l,int r,int L,int R){
    if(!y||l>R||r<L)return;
    if(l>=L&&r<=R){
        s2+=c[y].w-c[x].w;
        return;
    }
    int Mid=l+r>>1;
    Query1(c[x].l,c[y].l,l,Mid,L,R);
    Query1(c[x].r,c[y].r,Mid+1,r,L,R);
}
inline void Query2(int x,int y,int l,int r,int L,int R){
    if(!y||l>R||r<L)return;
    if(l>=L&&r<=R){
        s1+=c[y].s-c[x].s;s3+=c[y].t-c[x].t;
        return;
    }
    int Mid=l+r>>1;
    Query2(c[x].l,c[y].l,l,Mid,L,R);
    Query2(c[x].r,c[y].r,Mid+1,r,L,R);
}
int main(){
    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=Get(a[i]),f[i]=n;
    for(i=0;i<M;i++){
        Mi[0]=Mi[1]=n+1;z=0;
        for(j=n;j;j--){
            f[j]=Min(f[j],Mi[z^(a[j]&(1<<i)?1:0)]-1);
            if(b[j]==i)Mi[z]=j;z^=a[j]&(1<<i)?1:0;
        }
    }
    for(i=1;i<=n;i++)Insert(Rt[i],Rt[i-1],1,n,f[i],i,f[i]-i+1);
    scanf("%d",&Q);
    while(Q--){
        scanf("%d%d",&x,&y);
        x=(Ans%n+x)%n+1;y=(Ans%n+y)%n+1;
        if(x>y)swap(x,y);
        s1=s2=s3=0;
        Query1(Rt[x-1],Rt[y],1,n,x,y);
        if(y<n)Query2(Rt[x-1],Rt[y],1,n,y+1,n);
        Ans=s2-s3+1ll*(y+1)*s1;
        Print(Ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值