因为省选 (Day2T2) 来学习 (01 trie) 合并……
首先和线段树合并还有左偏树的 (merge) 一样的,就是一个 (merge) 函数
inline int merge(int x,int y)
{
if(!x||!y) return x+y;
int p=++tot;
for(int i=0;i<=25;++i) ch[p][i]=merge(ch[x][i],ch[y][i]);
return p;
}
然后就实现了 (trie) 树的合并(啊不是 (01 trie))
然后是一个应用题:
可以在一棵 (trie) 树上面删除一层的边,剩下的子树如果可以就像普通 (trie) 一样合并
求删掉哪一层剩下的点最少
这题的贡献法不太好想
删掉哪层剩下的点最少,那就考虑删掉的点最多的一层
考虑一下合并的过程我们会新开节点,如果新开一个相当于有一个被删掉了
然后就可以从上到下 (dfs) 处理这个过程了
接着是省选题:
其实不一定是 (01 trie) 合并做?
简述题意:
实现一个支持区间加 (1) ,区间异或和的数据结构、
其实难点在区间加一
然后如果在 (trie) 上面做就是交换左右儿子
(正好学了个板子……)
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=54e4;
int n,ans,res[N],v[N];
vector<int> g[N];
int tot,cnt[N*20],val[N*20],dep[N*20],ls[N*20],rs[N*20],rt[N],fa[N];
inline void push_up(int x){val[x]=val[ls[x]]^val[rs[x]]^((cnt[rs[x]]&1)<<dep[x]); return ;}
inline int merge(int x,int y)
{
if(!x||!y) return x+y;
cnt[x]+=cnt[y];
ls[x]=merge(ls[x],ls[y]); rs[x]=merge(rs[x],rs[y]);
push_up(x);
return x;
}
inline void add(int p)
{
if(!p||dep[p]>20) return ;
add(rs[p]); swap(rs[p],ls[p]);
push_up(p);
}
inline void insert(int &p,int val,int d)
{
if(!p) p=++tot,dep[p]=d;
++cnt[p]; if(d>20) return ;
if((val>>d)&1) insert(rs[p],val,d+1);
else insert(ls[p],val,d+1);
return push_up(p);
}
inline void dfs(int x)
{
int siz=g[x].size();
for(int i=0;i<siz;++i) dfs(g[x][i]),rt[x]=merge(rt[x],rt[g[x][i]]);
add(rt[x]); insert(rt[x],v[x],0);
res[x]=val[rt[x]];
return ;
}
signed main()
{
n=read(); for(int i=1;i<=n;++i) v[i]=read();
for(int i=2;i<=n;++i) fa[i]=read(),g[fa[i]].push_back(i); dfs(1);
for(int i=1;i<=n;++i) ans+=res[i]; printf("%lld
",ans);
return 0;
}
}
signed main(){return yspm::main();}