(笔记)一类异或问题 可持久化 01 Trie
01 trie
第 \(K\) 大/小异或问题在考虑集合时我们会想到整理成异或空间线性基,而考虑两两配对就可以大胆使用 01 trie,且该数据结构还有多数情况下平替权值线段树的功能,挂上去就知道大小关系,配合离散化使用和权值线段树简直一点差别也没有,而且好写。
这是一种字典树,加入数字先化为二进制形式,然后从高到低位加入,和普通字典树原理相同,只不过每个节点只有最多两个儿子,也采用边代表 0,1。这种二叉树结构让我们可以实现树上二分等多种功能。
血的教训:开空间一定要开够,\([0,30]\) 位可持久化下来每次会新建 \(32\) 个节点,至少要开 \(32n\)。
例题
P10471 最大异或对 The XOR Largest Pair
每位上尽量让查询值和当前值不同,如果成功了答案该位上就为 1。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,rt;
int ncnt;
struct Tre{int ch[2],cnt;}t[N*32];
void insert(int &p,int v,int id){
if(!p)p=++ncnt;
t[p].cnt++;
if(id<=-1)return ;
insert(t[p].ch[(v>>id)&1],v,id-1);
}
int query(int p,int v,int id){
if(id<=-1)return 0;
bool ex=(v>>id)&1;
if(t[t[p].ch[ex^1]].cnt>0)
return (1<<id)+query(t[p].ch[ex^1],v,id-1);
return query(t[p].ch[ex],v,id-1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
int a;cin>>a;
ans=max(ans,query(rt,a,30));
insert(rt,a,30);
}
cout<<ans;
return 0;
}
P4735 最大异或和
也是经典的两两异或最大值问题,可持久化 01 trie 即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+5;
int n,m,a[N],rt[N];
int ncnt;
struct Tre{int ch[2],cnt;}t[N*26];
void insert(int o,int &p,int v,int id){
if(!p)p=++ncnt;
t[p]=t[o];t[p].cnt++;
if(id<=-1)return ;
t[p].ch[(v>>id)&1]=0;
insert(t[o].ch[(v>>id)&1],t[p].ch[(v>>id)&1],v,id-1);
}
int query(int o,int p,int v,int id){
if(id<=-1)return 0;
bool ex=(v>>id)&1;
if(t[t[p].ch[ex^1]].cnt-t[t[o].ch[ex^1]].cnt>0)
return (1<<id)+query(t[o].ch[ex^1],t[p].ch[ex^1],v,id-1);
return query(t[o].ch[ex],t[p].ch[ex],v,id-1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i],a[i]^=a[i-1];
insert(rt[0],rt[0],0,24);
for(int i=1;i<=n;i++)insert(rt[i-1],rt[i],a[i],24);
while(m--){
char c;
int l,r,x;
cin>>c;
if(c=='A'){
cin>>x;a[++n]=x,a[n]^=a[n-1];
insert(rt[n-1],rt[n],a[n],24);
}
else {
cin>>l>>r>>x;
cout<<query((l-2>=0?rt[l-2]:0),rt[r-1],x^a[n],24)<<'\n';
}
}
return 0;
}
P5795 [THUSC 2015] 异或运算
考虑对 \(Y\) 建立可持久化 01 trie,暴力扫 \(X\),每次多树上二分即可,每次分别枚举答案是否可能有第 \(d\) 位。这里如果外面写个二分答案复杂度多了一只 \(\log\),无法通过。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=3e5+5;
int n,m,X[N],Y[N];
int ncnt,rt[N],st[N][2];
struct Tre{int ch[2],cnt;}t[N*32];
void insert(int o,int &p,int v,int id){
if(!p)p=++ncnt;
t[p]=t[o];t[p].cnt++;
if(id<=-1)return ;
t[p].ch[(v>>id)&1]=0;
insert(t[o].ch[(v>>id)&1],t[p].ch[(v>>id)&1],v,id-1);
}
int ef(int u,int d,int L,int R,int k){
for(int i=u;i<=d;i++)st[i][0]=rt[L-1],st[i][1]=rt[R];
int res=0;
for(int id=30;id>=0;id--){
int cntl=0;
bool wk=0;
for(int i=u;i<=d;i++){
bool v=(X[i]>>id)&1;
cntl+=t[t[st[i][1]].ch[v]].cnt-t[t[st[i][0]].ch[v]].cnt;
}
if(cntl<k)k-=cntl,res+=(1<<id),wk=1;
else wk=0;
for(int i=u;i<=d;i++){
bool v=(X[i]>>id)&1;
st[i][0]=t[st[i][0]].ch[wk^v],
st[i][1]=t[st[i][1]].ch[wk^v];
}
}
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>X[i];
for(int i=1;i<=m;i++)cin>>Y[i];
for(int i=1;i<=m;i++)insert(rt[i-1],rt[i],Y[i],30);
int P;cin>>P;
while(P--){
int xa,ya,xb,yb,k;
cin>>xa>>xb>>ya>>yb>>k;
cout<<ef(xa,xb,ya,yb,(yb-ya+1)*(xb-xa+1)-k+1)<<'\n';
}
return 0;
}
P4592 [TJOI2018] 异或
笛卡尔积题,随便搞个 DFS 序或者 01 trie 合并(?)维护子树信息,链信息用树上差分的可持久化 01 trie 即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,qn,a[N],ans[N],rt[N],mrt[N],f[N][20],dep[N];
vector<int>q1[N],G[N];
struct Q{int op,x,y,z;}q[N];
int ncnt;
struct Tre{int ch[2],cnt;}t[N*61];
void insert(int o,int &p,int v,int id){
if(!p)p=++ncnt;
t[p]=t[o];t[p].cnt++;
if(id<=-1)return ;
t[p].ch[(v>>id)&1]=0;
insert(t[o].ch[(v>>id)&1],t[p].ch[(v>>id)&1],v,id-1);
}
void ins(int &p,int v,int id){
if(!p)p=++ncnt;
t[p].cnt++;
if(id<=-1)return ;
ins(t[p].ch[(v>>id)&1],v,id-1);
}
int query(int o,int p,int v,int id){
if(id<=-1)return 0;
bool ex=(v>>id)&1;
if(t[t[p].ch[ex^1]].cnt-t[t[o].ch[ex^1]].cnt>0)
return (1<<id)+query(t[o].ch[ex^1],t[p].ch[ex^1],v,id-1);
return query(t[o].ch[ex],t[p].ch[ex],v,id-1);
}
void merge(int o,int &p,int id){
if(!o||!p){p=o|p;return ;}
t[p].cnt+=t[o].cnt;
merge(t[o].ch[0],t[p].ch[0],id-1);
merge(t[o].ch[1],t[p].ch[1],id-1);
}
void dfs(int u,int fa){
dep[u]=dep[fa]+1;
insert(rt[fa],rt[u],a[u],30);
for(int v:G[u]){
if(v==fa)continue;
f[v][0]=u;
for(int j=1;(1<<j)<=dep[u];j++)
f[v][j]=f[f[v][j-1]][j-1];
dfs(v,u);
merge(mrt[v],mrt[u],30);
}
ins(mrt[u],a[u],30);
for(int v:q1[u])
ans[v]=query(0,mrt[u],q[v].z,30);
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=17;i>=0;i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y)return x;
for(int i=17;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>qn;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=qn;i++){
cin>>q[i].op>>q[i].x;
if(q[i].op==2)cin>>q[i].y>>q[i].z;
else cin>>q[i].z,q1[q[i].x].push_back(i);
}
dfs(1,0);
for(int i=1;i<=qn;i++)
if(q[i].op==2){
int lca=LCA(q[i].x,q[i].y);
int r1=query(rt[f[lca][0]],rt[q[i].x],q[i].z,30);
int r2=query(rt[f[lca][0]],rt[q[i].y],q[i].z,30);
ans[i]=max(r1,r2);
}
for(int i=1;i<=qn;i++)cout<<ans[i]<<'\n';
return 0;
}
P4098 [HEOI2013] ALO
用到了基础常见 DP 优化以及图上问题、思维题、数据结构题 trick 计数类问题优化。
处理下来直接可持久化 01 trie 查询即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5e4+5;
int n,a[N],rt[N],ncnt;
struct trie{int cnt,ch[2];}t[N*32];
void insert(int o,int &p,int v,int id){
if(!p)p=++ncnt;
t[p]=t[o];t[p].cnt++;
if(id<0)return ;
bool q=(v>>id)&1;
t[p].ch[q]=0;
insert(t[o].ch[q],t[p].ch[q],v,id-1);
}
int query(int o,int p,int v,int id){
if(id<0)return 0;
bool q=(v>>id)&1;
if(t[t[p].ch[q^1]].cnt-t[t[o].ch[q^1]].cnt>0)
return query(t[o].ch[q^1],t[p].ch[q^1],v,id-1)+(1<<id);
return query(t[o].ch[q],t[p].ch[q],v,id-1);
}
int fir,las;
int pre[N],suf[N];
int rk[N],ans;
bool cmp(int x,int y){return a[x]<a[y];}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
fir=0,las=n+1;
suf[fir]=1;pre[las]=n;
pre[fir]=0,suf[las]=n+1;
for(int i=1;i<=n;i++){
rk[i]=i;
pre[i]=i-1;
suf[i]=i+1;
cin>>a[i],insert(rt[i-1],rt[i],a[i],30);
}
sort(rk+1,rk+1+n,cmp);
for(int i=1;i<=n;i++){
int id=rk[i];
int l=pre[id],r=suf[id];
suf[l]=r;pre[r]=l;
if(l!=fir)ans=max(ans,query(rt[pre[l]],rt[r-1],a[id],30));
if(r!=las)ans=max(ans,query(rt[l],rt[suf[r]-1],a[id],30));
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号