P4735 最大异或和
题意:
一个非负序列a,初始长度为n,有两种操作:
- A x,在序列中添加x,n加一
- Q l r x,询问操作,询问一个位置p,p满足l<=p<=r,使得p到位置n的的异或和与x的异或值最大,输出最大值
题解:
我们来看这个查询操作,p∈[l,r],我们设整个区间的异或值为tot,
a[p]⊕a[p+1]⊕.....a[N]a[p] ⊕ a[p+1] ⊕ .....a[N]a[p]⊕a[p+1]⊕.....a[N]=tot⊕(a[1]⊕a[2].....⊕a[p−1])tot ⊕ (a[1] ⊕ a[2].....⊕a[p-1])tot⊕(a[1]⊕a[2].....⊕a[p−1])
相当于我们巧妙的将后缀问题转换成前缀问题,那就是找一个前缀s[y]⊕(x⊕tot)的值最大,y属于[l-1,r-1]。转化成可持久化01Trie经典模型:
给定(l,r,x),在序列[l,r]区间中选一个数a[i],使得a[i]⊕x最大
然后按照可持久化的套路,我们将所有前缀异或和s[i]建立可持久化01Trie,算出(x⊕tot)的值后在rt[l-2]到rt[r-1]这两颗树上走一边,寻找最大值
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){ll s=0,w=1ll;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
void rd_txt(){#ifdef ONLINE_JUDGE#elsefreopen("in.txt","r",stdin);#endif
}
const int maxn=6e5+9;
struct tree{int cnt;int ch[2];
}tr[maxn*30];
int rtnum=0;
int a[maxn];
int rt[maxn];
int tot=0;
void insert(int now,int pre,int x){for(int i=30;i>=0;i--){int c=((x>>i)&1);tr[now].ch[c^1]=tr[pre].ch[c^1];tr[now].ch[c]=++rtnum;now=tr[now].ch[c];pre=tr[pre].ch[c];tr[now].cnt=tr[pre].cnt+1;}
}
int get(int L,int R,int x){int sum=0;for(int i=30;i>=0;i--){int c=((x>>i)&1);if(tr[tr[R].ch[c^1]].cnt>tr[tr[L].ch[c^1]].cnt){sum+=(1<<i);L=tr[L].ch[c^1];R=tr[R].ch[c^1]; }else {L=tr[L].ch[c];R=tr[R].ch[c]; }}return sum;
}int main()
{rd_txt();int n,m;cin>>n>>m;rt[0]=++rtnum;insert(rt[0],0,0);tot=0;for(int i=1;i<=n;i++){cin>>a[i];tot^=a[i];rt[i]=++rtnum;insert(rt[i],rt[i-1],tot);}for(int i=1;i<=m;i++){string op;cin>>op;int x;if(op=="A"){cin>>x;tot^=x;rt[++n]=++rtnum;insert(rt[n],rt[n-1],tot);}else if(op=="Q"){int l,r,x;cin>>l>>r>>x;l--;r--;if(l==0)printf("%d\n",get(0,rt[r],tot^x));else printf("%d\n",get(rt[l-1],rt[r],tot^x));}}return 0;
}