Trie树合并

因为省选 \(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\)

然后是一个应用题:

\(CF778C\)

可以在一棵 \(trie\) 树上面删除一层的边,剩下的子树如果可以就像普通 \(trie\) 一样合并

求删掉哪一层剩下的点最少


这题的贡献法不太好想

删掉哪层剩下的点最少,那就考虑删掉的点最多的一层

考虑一下合并的过程我们会新开节点,如果新开一个相当于有一个被删掉了

然后就可以从上到下 \(dfs\) 处理这个过程了


接着是省选题:

P6623 [省选联考 2020 A 卷] 树

其实不一定是 \(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\n",ans); 
		return 0;
	}
} 
signed main(){return yspm::main();}

猜你喜欢

转载自www.cnblogs.com/yspm/p/13193376.html