bzoj 3261 最大异或和 可持久化字典树(01树)

题目传送门

思路:

  由异或的性质可得,题目要求的式子可以转化成求$max(pre[n]^x^pre[i])$,$pre[i]$表示前缀异或和,那么我们现在就要求出这个东西,所以用可持久化字典树来求,每次贪心的往相反的方向看是否有值,具体看代码即可,模板题,注意最好先插入一个0,查询区间的$(l,r)$也要注意一下端点,记住我们要的是前缀。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn=600010;
int sum[maxn*30],tr[maxn*30][2],cnt,root[maxn];
int pre,n,m,l,r,d[30];
void split(int x)
{
    int len=0;
    while(x>0){
        d[len++]=x%2;
        x/=2;
    }
    while(len<27)d[len++]=0;
}
inline void update(int &rt,int las){
    sum[rt=++cnt]=sum[las]+1;
    int tmp=rt;
    for(int i=27;i>=0;i--){
        tr[tmp][d[i]^1]=tr[las][d[i]^1];
        tr[tmp][d[i]]=++cnt,las=tr[las][d[i]];
        sum[tmp=cnt]=sum[las]+1;        
    }
}
int query(int l,int r){
    int ans=0;
    for(int i=27;i>=0;i--)
    {
        if(sum[tr[r][d[i]^1]]-sum[tr[l][d[i]^1]]>0){
            ans|=(1<<i);
            r=tr[r][d[i]^1],l=tr[l][d[i]^1];
        }else{
            r=tr[r][d[i]],l=tr[l][d[i]];
        }
    }
    return ans;
}
int main(){
    while(cin>>n>>m)
    {
        pre=0;
        split(0);
        update(root[1],root[0]);
        n++;
        for(int i=2;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            pre^=x;
            split(pre);
            update(root[i],root[i-1]);
        }
        char op[10];
        for(int i=1;i<=m;i++)
        {
            scanf("%s",op);
            if(op[0]=='A'){
                n++;
                int x;
                scanf("%d",&x);
                pre^=x;
                split(pre);
                update(root[n],root[n-1]);
            }else{
                int l,r,x;
                scanf("%d%d%d",&l,&r,&x);
                split(pre^x);
                printf("%d
",query(root[l-1],root[r]));
            }
        }
    }
    
}
View Code
原文地址:https://www.cnblogs.com/mountaink/p/10573677.html