[bzoj3261][可持久化Tire]最大异或和

Description

给定一个非负整数序列{a},初始长度为N。 有M个操作,有以下两种操作类型:
1、Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1。 2、Qlrx:询问操作,你需要找到一个位置p,满足l<=p<=r,使得:
a[p] xor a[p+1] xor … xor a[N] xor x 最大,输出最大是多少。

Input

第一行包含两个整数 N ,M,含义如问题描述所示。 第二行包含 N个非负整数,表示初始的序列 A 。 接下来
M行,每行描述一个操作,格式如题面所述。

Output

假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。

Sample Input

5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6

Sample Output

4
5
6

HINT

对于测试点 1-2,N,M<=5 。
对于测试点 3-7,N,M<=80000 。
对于测试点 8-10,N,M<=300000 。
其中测试点 1, 3, 5, 7, 9保证没有修改操作。
0<=a[i]<=10^7。

题解

哇超级开心自己看出来的自己写的可持久化01Tire
嘻嘻嘻这个东西还是在之前写的一篇文章里被大佬Lazer2001D的时候学的
怎么做呢?
设前i位的xor和为sum[i]
由于xor的运算律,那么对于位置l-1~r-1,我们其实就是要找一个k,使得(sum[n]^x)^sum[k]最大
因为sum[n]=sum[k]^(k+1~n的xor和)嘛,那么满足交换律结合律的情况下,sum[k]^sum[k]=0,所以说最后结果就是(k+1~n的xor和)^x。这时候你也可以明白了为什么要是l-1~r-1了,因为选择了位置k,实际上选择的是k+1~n这一段嘛
那对于sum[i]建可持久化Tire,用主席树的思想去查找就好了
注意内存要开大,想想m都是修改的情况??
因为这个RE了几发

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
struct node
{
    int son[2],s;
}tr[31100010];int tot;
int tmp[30],cnt[30],len;
void get(int x)
{
    int ln=0;
    while(x)
    {
        cnt[++ln]=x%2;
        x/=2;
    }
    int lenth=ln;
    memset(tmp,0,sizeof(tmp));
    for(int i=25;ln!=0;i--)tmp[i]=cnt[lenth-ln+1],ln--;
    //for(int i=1;i<=25;i++)printf("%d",tmp[i]);
}
int n,m,rt[610000];
void add(int &p,int k)
{
    if(p==0)p=++tot;
    if(k!=1)tr[p].s++;
    if(k>25)return ;
    if(tmp[k]==0)add(tr[p].son[0],k+1);
    else add(tr[p].son[1],k+1);
}
void merge(int &x,int y)
{
    if(x==0){x=y;return ;}
    if(y==0)return ;
    tr[x].s+=tr[y].s;
    merge(tr[x].son[0],tr[y].son[0]);
    merge(tr[x].son[1],tr[y].son[1]);
}
char ss[10];
int sum,ret,ans[30];
void sol(int l,int r,int k)
{
    int sx=tr[tr[r].son[0]].s-tr[tr[l].son[0]].s,sy=tr[tr[r].son[1]].s-tr[tr[l].son[1]].s;
    if(k==26)return ;
    if(tmp[k]==0)
    {
        if(sy>0)ans[k]=1,sol(tr[l].son[1],tr[r].son[1],k+1);
        else ans[k]=0,sol(tr[l].son[0],tr[r].son[0],k+1);
    }
    else
    {
        if(sx>0)ans[k]=1,sol(tr[l].son[0],tr[r].son[0],k+1);
        else ans[k]=0,sol(tr[l].son[1],tr[r].son[1],k+1);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    sum=0;tot=0;add(rt[1],1);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);sum^=x;
        get(sum);
        add(rt[i+1],1);
        merge(rt[i+1],rt[i]);
    }
    n++;
    while(m--)
    {
        int u,v,x;
        scanf("%s%d",ss+1,&u);
        if(ss[1]=='A')
        {
            sum^=u;get(sum);
            add(rt[++n],1);merge(rt[n],rt[n-1]);
        }
        else
        {
            scanf("%d%d",&v,&x);u++;v++;
            ret=sum^x;get(ret);
            memset(ans,0,sizeof(ans));
            sol(rt[max(u-2,0)],rt[v-1],1);
            LL pp=0;
            for(int i=1;i<=25;i++)if(ans[i]==1)pp+=(1LL<<(25-i));
            printf("%lld\n",pp);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值