把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷6623】[省选联考 2020 A 卷] 树(有趣的Trie树题)

点此看题面

大致题意: 给定一棵树,节点\(i\)点权为\(v_i\)。定义\(val(x)=xor_y(v_y+d(x,y))\),其中\(y\)\(x\)子树内的点(包括\(x\)自身),\(d(x,y)\)\(x,y\)的树上距离,求\(\sum_{i=1}^nval(i)\)

前言

\(Trie\)树的这种套路之前接触过,因此很快就做了出来。

由于这是一道挺有趣的题,于是就写了写。

题意转化

由于\(val(x)\)的求解对象是子树内的点,容易想到先求出子树内的答案,然后通过数据结构合并得到父节点的答案。

考虑子节点的答案到了父节点,所有距离加\(1\),同时还要统计上父节点自身的贡献。

也就是要支持下列几项操作:增加一个数、合并、求总异或和、给所有数加\(1\)

看到异或想到\(Trie\)树,且前三项操作显然都很容易就可以用\(Trie\)树维护。

而最后的给所有数加\(1\),其实有一个套路做法。

给所有数加\(1\)

与一般\(Trie\)树相反,考虑我们从低位到高位建一棵\(Trie\)树。

每次加\(1\),原先的\(0\)变成了\(1\),原先的\(1\)变成了\(0\),然后还要进位加\(1\)

其实也就是,交换左右儿子,然后递归继续给新的左儿子加\(1\)

于是这道题就做完了,具体实现详见代码。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 525010
using namespace std;
int n,a[N+5],f[N+5],Rt[N+5],ans[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
class Trie
{
	private:
		#define LN 25
		int Nt;struct node {int V,S[2];}O[N*LN<<1];
	public:
		I void Ins(int& rt,CI x,CI d=0)//增加一个数
		{
			if(d==LN) return;!rt&&(rt=++Nt),O[rt].V^=1,Ins(O[rt].S[x&1],x>>1,d+1);
		}
		I void U(CI rt,int& res,CI d=0)//给所有数加1
		{
			if(!rt) return;res^=(O[O[rt].S[0]].V^O[O[rt].S[1]].V)<<d,//更新这一位的答案
			swap(O[rt].S[0],O[rt].S[1]),U(O[rt].S[0],res,d+1);//交换左右儿子,接着递归处理进位
		}
		I void Merge(int& x,CI y)//Trie树合并
		{
			if(!x||!y) return (void)(x|=y);O[x].V^=O[y].V,
			Merge(O[x].S[0],O[y].S[0]),Merge(O[x].S[1],O[y].S[1]);
		}
}T;
int main()
{
	RI i;for(F.read(n),i=1;i<=n;++i) F.read(a[i]);for(i=2;i<=n;++i) F.read(f[i]);
	long long t=0;for(i=n;i;--i) T.U(Rt[i],ans[i]),T.Ins(Rt[i],a[i]),//子树内所有点距离加1,然后统计当前点贡献
		ans[i]^=a[i],t+=ans[i],i^1&&(T.Merge(Rt[f[i]],Rt[i]),ans[f[i]]^=ans[i]);//计算答案,然后把Trie树合并给父节点
	return printf("%lld\n",t),0;
}
posted @ 2020-07-16 20:13  TheLostWeak  阅读(177)  评论(0编辑  收藏  举报