(笔记)一类异或问题 可持久化 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;
}
posted @ 2025-08-05 10:18  TBSF_0207  阅读(24)  评论(0)    收藏  举报