【洛谷P6623】树

题目

题目链接:https://www.luogu.com.cn/problem/P6623
给定一棵 \(n\) 个结点的有根树 \(T\),结点从 \(1\) 开始编号,根结点为 \(1\) 号结点,每个结点有一个正整数权值 \(v_i\)
\(x\) 号结点的子树内(包含 \(x\) 自身)的所有结点编号为 \(c_1,c_2,\dots,c_k\),定义 \(x\) 的价值为:

\[val(x)=(v_{c_1}+d(c_1,x)) \oplus (v_{c_2}+d(c_2,x)) \oplus \cdots \oplus (v_{c_k}+d(c_k, x)) \]

其中 \(d(x,y)\) 表示树上 \(x\) 号结点与 \(y\) 号结点间唯一简单路径所包含的边数,\(d(x,x) = 0\)\(\oplus\) 表示异或运算。
请你求出 \(\sum\limits_{i=1}^n val(i)\) 的结果。

思路

考虑从答案从 \(x\) 的子节点如何转移到 \(x\) 上来:显然是每一个节点的权值加一后再异或起来。
把每一个节点的权值 + 到目前根的距离转成二进制,由低位到高位扔进一棵 Trie 中,那么把所有子树内的点权值加一,其实就是沿着 Trie 边权为 1 的点走下去,并且把沿路遇到的 0 给变成 1,再把沿路的 1 变为 0。那么其实就是将遍历到的节点的左右子树交换了。
插入自己本身的权值简单,那么我们就成功搞定加一操作和插入操作。我们只需要将 \(x\) 的各个子节点的 Trie 合并到 \(x\) 上。类似于线段树合并。
时间复杂度 \(O(n\log n)\)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=525020,LG=25;
int n,tot,a[N],head[N],rt[N];
ll ans;

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

struct Trie
{
	int tot,lc[N*LG],rc[N*LG],val[N*LG],size[N*LG];
	
	void pushup(int x,int dep)
	{
		val[x]=val[lc[x]]^val[rc[x]];
		if (size[rc[x]]&1) val[x]^=(1<<dep);
	}
	
	int merge(int x,int y,int dep)
	{
		if (!x || !y) return x+y;
		size[x]+=size[y];
		lc[x]=merge(lc[x],lc[y],dep+1);
		rc[x]=merge(rc[x],rc[y],dep+1);
		pushup(x,dep);
		return x;
	}
	
	void update(int x,int dep)
	{
		if (!x) return;
		swap(lc[x],rc[x]);
		update(lc[x],dep+1);
		pushup(x,dep);
	}
	
	int ins(int x,int val,int dep)
	{
		if (dep>22) return 0;
		if (!x) x=++tot;
		size[x]++;
		if (val&(1<<dep)) rc[x]=ins(rc[x],val,dep+1);
			else lc[x]=ins(lc[x],val,dep+1);
		pushup(x,dep);
		return x;
	}
}trie;

void dfs(int x)
{
	for (int i=head[x];~i;i=e[i].next)
	{
		dfs(e[i].to);
		rt[x]=trie.merge(rt[x],rt[e[i].to],0);
	}
	trie.update(rt[x],0);
	rt[x]=trie.ins(rt[x],a[x],0);
	ans+=trie.val[rt[x]];
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for (int i=2,x;i<=n;i++)
	{
		scanf("%d",&x);
		add(x,i);
	}
	dfs(1);
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/13199533.html