可持久化01Trie学习笔记
前置知识
字典树。
定义
可持久化 Trie 的方式和可持久化线段树的方式是相似的,即每次只修改被添加或值被修改的节点,而保留没有被改动的节点,在上一个版本的基础上连边,使最后每个版本的 Trie 树的根遍历所能分离出的 Trie 树都是完整且包含全部信息的。
大部分的可持久化 Trie 题中,Trie 都是以 01-Trie 的形式出现的。
引入
设 \(s_i=a_i\oplus a_{i+1}\oplus\cdots\oplus a_n\)。
询问即在 \([l,r]\) 中找到 \(\oplus x\) 最大的树。
考虑 01-Trie 把所有 \(s_i\) 扔上去,把 \(x\) 放到树上跑。每次判断某节点 \(p\) 是否有 \([l,r]\) 的节点经过。
考虑前缀和记录。设 \(val_{i,p}\) 表示 \([1,i]\) 中经过 \(p\) 的数量,即判断 \(val_{r,p}-val_{l-1,p}\) 是否为正。
空间爆炸,可持久化。
考虑加入一个点 \(u\),最多 \(\log V\) 个节点发生改变,新建一下即可。
由于这 \(n\) 个数的加入本身就是一个一个加的,所以操作 1 就简单了。但是加 \(1\) 会导致不好维护后缀异或和,改成前缀,则为 \(s_n\oplus x\) 异或上一个 \([l-1,r-1]\) 的 \(s_i\) 的最大值。
定义
| rt[i] | cnt | val[u] | c[u][0/1] |
|---|---|---|---|
| 第 \(i\) 棵树根节点 | 节点个数 | 经过 \(u\) 节点的前缀 \(1,i^{*}\) | 节点 \(u\) 左/右儿子编号 |
| \(*\):具体要看 \(u\) 节点在哪棵树上。 |
注意,如果 \(l=1\),则不可能查到 \(-1\) 的树。
考虑整体右移 \(1\),若 \(l=1\) 即代表可以有 \(0\) 这个选项,在一开始插入 \(0\) 即可。
插入
void ins(int x)//将数值x插入b树
{
rt[++tot]=++cnt;
int u=rt[tot],u2=rt[tot-1];//上一个版本是b-1
ff(i,31,0)
{
int v=(x>>i)&1;
c[u][v]=++cnt;
c[u][!v]=c[u2][!v];
u=c[u][v],u2=c[u2][v];
val[u]=val[u2]+1;
}
}
询问
int ask(int l,int r,int x)
{
int ans=0;
int u=rt[r],u2=rt[l-1];
ff(i,31,0)
{
int v=(x>>i)&1;
if(val[c[u][!v]]-val[c[u2][!v]]>0)
{
u=c[u][!v],u2=c[u2][!v];
ans+=1<<i;
}
else u=c[u][v],u2=c[u2][v];
}
return ans;
}
完整代码
#include<bits/stdc++.h>
#define sd std::
//#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int V=2e7+10,N=3e5+10;
int n,m,cnt,c[V][2],val[V],rt[V],tot;
void ins(int x)//将数值x插入b树
{
rt[++tot]=++cnt;
int u=rt[tot],u2=rt[tot-1];//上一个版本是b-1
ff(i,31,0)
{
int v=(x>>i)&1;
c[u][v]=++cnt;
c[u][!v]=c[u2][!v];
u=c[u][v],u2=c[u2][v];
val[u]=val[u2]+1;
}
}
int ask(int l,int r,int x)
{
int ans=0;
int u=rt[r],u2=rt[l-1];
ff(i,31,0)
{
int v=(x>>i)&1;
if(val[c[u][!v]]-val[c[u2][!v]]>0)
{
u=c[u][!v],u2=c[u2][!v];
ans+=1<<i;
}
else u=c[u][v],u2=c[u2][v];
}
return ans;
}
void solve()
{
ins(0);
n=read(),m=read();
int sum=0;
F(i,1,n) ins(sum^=read());
while(m--)
{
char op[5];
scanf("%s",op);
if(op[0]=='A')
{
ins(sum^=read());
}
else
{
int l=read(),r=read();
put(ask(l,r,sum^read()));
}
}
}
int main()
{
int T=1;
// T=read();
while(T--) solve();
return 0;
}
例题
询问1可以维护dfs序的可持久化01Trie,询问二可以维护每个点到根的可持久化01Trie。
对于询问1,dfs序上找区间即可。对于询问2用树上差分即可。
#include<bits/stdc++.h>
#define sd std::
//#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e5+10,V=1.5e7+10;
int n,q,rt[N],val[V],c[V][2][2];
int rt1[N],rt2[N];//·?±e′ú±í?ùμ?xoídfsDòμ?
int tot,d[N],dfn[N],num,rev[N];
struct node
{
int nex;
int to;
}a[N<<1];
int cnt,head[N];
void add(int u,int v)
{
a[++cnt].nex=head[u];
head[u]=cnt;
a[cnt].to=v;
}
int fa[N],dep[N],f[N][33],siz[N];
void insert(int x,int op)//2?è?d[x]
{
int u1,u2;
if(op) rt1[x]=++tot,u1=rt1[x],u2=rt1[fa[x]];//op=1,rt1是处理询问2的
else rt2[x]=++tot,u1=rt2[x],u2=rt2[rev[dfn[x]-1]];
x=d[x];
ff(i,31,0)
{
int v=(x>>i)&1;
c[u1][v][op]=++tot;
val[c[u1][v][op]]=val[c[u2][v][op]]+1;
c[u1][!v][op]=c[u2][!v][op];
u1=c[u1][v][op],u2=c[u2][v][op];
}
}
void dfs(int u,int da)
{
dfn[u]=++num;rev[num]=u;dep[u]=dep[da]+1;
f[u][0]=fa[u]=da;
F(i,0,31) f[u][i+1]=f[f[u][i]][i];
insert(u,1);
insert(u,0);
siz[u]=1;
for(int i=head[u];i;i=a[i].nex)
{
int v=a[i].to;
if(v==da) continue;
dfs(v,u);
siz[u]+=siz[v];
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) sd swap(x,y);
ff(i,31,0)
{
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
}
ff(i,31,0)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int ask1(int b,int x)
{
int u1=rt2[rev[dfn[b]-1]],u2=rt2[rev[dfn[b]+siz[b]-1]];
int ans=0;
ff(i,31,0)
{
int v=(x>>i)&1;
if(val[c[u2][!v][0]]-val[c[u1][!v][0]]>0)
{
ans+=1<<i;
u1=c[u1][!v][0];
u2=c[u2][!v][0];
}
else u1=c[u1][v][0],u2=c[u2][v][0];
}
return ans;
}
int ask2(int b,int x,int y)
{
int u1=rt1[x],u2=rt1[y],u3=rt1[fa[lca(x,y)]],u4=rt1[lca(x,y)];
int ans=0;
ff(i,31,0)
{
int v=(b>>i)&1;
if(val[c[u1][!v][1]]+val[c[u2][!v][1]]-val[c[u3][!v][1]]-val[c[u4][!v][1]]>0)
{
ans+=1<<i;
u1=c[u1][!v][1];
u2=c[u2][!v][1];
u3=c[u3][!v][1];
u4=c[u4][!v][1];
}
else
{
u1=c[u1][v][1];
u2=c[u2][v][1];
u3=c[u3][v][1];
u4=c[u4][v][1];
}
}
return ans;
}
void solve()
{
n=read(),q=read();
F(i,1,n) d[i]=read();
F(i,1,n-1)
{
int x=read(),y=read();
add(x,y);
add(y,x);
}
dfs(1,0);
while(q--)
{
int op=read(),x=read(),y,z;
if(op==1)
{
z=read();
put(ask1(x,z));
}
else if(op==2)
{
y=read(),z=read();
put(ask2(z,x,y));
}
}
}
int main()
{
int T=1;
// T=read();
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号