可持久化Trie
可持久化Trie--区间异或最值问题
应用:在 \(O(logn)\) 时间复杂度解决 查询区间 \([l,r]\) 内与数 \(x\) 异或的最大值。
插入操作:把 \(n\) 个整数插入 \(01Trie\),生成 \(n\) 个版本的可持久化树。
查询操作:利用前缀和与差分的思想,用两颗前缀 \(Trie\) 树相减得到该区间的 \(Trie\) 树。查询的时候,利用贪心思想,尽量与当前位不同的地方跳即可。
模板:
//可持久化Trie
struct Trie{
int ch[N<<5][2];
int rt[N];
int sz[N<<5];
int idx=0,cnt=0;
void insert(int v){
rt[++idx]=++cnt;//新根开点
int x=rt[idx-1];//旧版
int y=rt[idx];
for(int i=30;i>=0;i--){
int j=v>>i&1;
ch[y][j^1]=ch[x][j^1];//异位继承
ch[y][j]=++cnt;//新位
x=ch[x][j];y=ch[y][j];
sz[y]=sz[x]+1;
}
}
int query(int x,int y,int v){
int ret=0;
for(int i=30;i>=0;i--){
int j=(v>>i&1);
if(sz[ch[y][j^1]]>sz[ch[x][j^1]])
x=ch[x][j^1],y=ch[y][j^1],ret|=(1LL<<i);
else
x=ch[x][j],y=ch[y][j];
}
return ret;
}
}trie;
查询 \([l,r]\) 与 \(x\) 的最大异或值时: \(query(trie.rt[l-1],trie.rt[r])\)
P4592 [TJOI2018] 异或 树链剖分+可持久化Trie
一道模板题,解决区间异或最大值--可持久化Trie。然后用树链剖分把树上问题转化成链上问题即可。
注意插入的时候要按照 \(dfs\) 序插入Trie中。
#include<bits/stdc++.h>
using namespace std;
using i64=long long;
const int N =1e5+10;
int a[N];
//可持久化Trie
struct Trie{
int ch[N<<5][2];
int rt[N];
int sz[N<<5];
int idx=0,cnt=0;
void insert(int v){
rt[++idx]=++cnt;//新根开点
int x=rt[idx-1];//旧版
int y=rt[idx];
for(int i=30;i>=0;i--){
int j=v>>i&1;
ch[y][j^1]=ch[x][j^1];//异位继承
ch[y][j]=++cnt;//新位
x=ch[x][j];y=ch[y][j];
sz[y]=sz[x]+1;
}
}
int query(int x,int y,int v){
int ret=0;
for(int i=30;i>=0;i--){
int j=(v>>i&1);
if(sz[ch[y][j^1]]>sz[ch[x][j^1]])
x=ch[x][j^1],y=ch[y][j^1],ret|=(1LL<<i);
else
x=ch[x][j],y=ch[y][j];
}
return ret;
}
}trie;
//树链剖分
vector<int> e[N];
int fa[N],dep[N],son[N],sz[N],id[N],w[N];
int top[N],dfn;
//预处理各个信息
void dfs1(int u,int father){
fa[u]=father;
dep[u]=dep[father]+1;
sz[u]=1;
for(auto v:e[u]){
if(v==father) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
}
void dfs2(int u,int tp){
id[u]=++dfn;
w[dfn]=a[u];
top[u]=tp;
if(!son[u]) return;
dfs2(son[u],tp);
for(auto v:e[u]){
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int qRange(int u,int v,int val){
int res=0,l,r;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
//线段树区间查询[id[top[u]],id[u]]
l=trie.rt[id[top[u]]-1];
r=trie.rt[id[u]];
res=max(res,trie.query(l,r,val));
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
//加上两点之间的答案[id[u],id[v]]
l=trie.rt[id[u]-1];
r=trie.rt[id[v]];
res=max(res,trie.query(l,r,val));
return res;
}
int qSon(int u,int v){
//线段树查询[id[u],id[u]+sz[u]-1]
int l=trie.rt[id[u]-1],r=trie.rt[id[u]+sz[u]-1];
return trie.query(l,r,v);
}
void Showball(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1,-1);
dfs2(1,1);
vector<int> p(n+1);
iota(p.begin(),p.end(),0);
sort(p.begin()+1,p.end(),[&](int x,int y){
return id[x]<id[y];
});
for(int i=1;i<=n;i++){
trie.insert(a[p[i]]);
}
while(q--){
int op;
cin>>op;
if(op==1){
int x,z;
cin>>x>>z;
cout<<qSon(x,z)<<"\n";
}else{
int x,y,z;
cin>>x>>y>>z;
cout<<qRange(x,y,z)<<"\n";
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t=1;
//cin>>t;
while(t--){
Showball();
}
return 0;
}