联合省选2020 树

给定一棵 (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_{i=1}^n val(i)) 的结果。

(100\%) 的数据:(1le n, v_i le 525010, 1le p_ile n)

Trie

https://blog.csdn.net/qq_39854734/article/details/106946271
https://blog.csdn.net/qq_33229466/article/details/106927385

考虑用 01-Trie 来维护所有(v(c_j)+d(c_j,x)),并启发式合并。

01-Trie 需要支持:插入一个数;求全局异或和;全局加一。

前两个操作容易维护(全局异或和需要每个节点记录其子树里所有数在一些位的异或和);

为了支持第三个操作,我们把数按从低位到高位放进 01-Trie,每次交换左右儿子,然后递归处理左(交换前是右)儿子,然后更新自己这个节点维护的异或和。

时间复杂度(O(nlog n))

CO int N=53e4;
int a[N],sum[N][21];
vector<int> to[N];

int root[N],tot;
int ch[N*25][2],siz[N*25];

void insert(int&x,int i,int v,int t){
	if(!x) x=++tot;
	++siz[x];
	if(i==21) return;
	if(v>>i&1) insert(ch[x][1],i+1,v,t),++sum[t][i];
	else insert(ch[x][0],i+1,v,t);
}
void modify(int x,int i,int t){
	if(!x or i==21) return;
	sum[t][i]+=siz[ch[x][0]]-siz[ch[x][1]];
	swap(ch[x][0],ch[x][1]);
	modify(ch[x][0],i+1,t);
}
int merge(int x,int y){
	if(!x or !y) return x+y;
	siz[x]+=siz[y];
	ch[x][0]=merge(ch[x][0],ch[y][0]);
	ch[x][1]=merge(ch[x][1],ch[y][1]);
	return x;
}

int64 ans;

void dfs(int x){
	insert(root[x],0,a[x],x);
	for(int y:to[x]){
		dfs(y);
		modify(root[y],0,y);
		root[x]=merge(root[x],root[y]);
		for(int i=0;i<=20;++i) sum[x][i]+=sum[y][i];
	}
	for(int i=0;i<=20;++i)if(sum[x][i]&1) ans+=1<<i;
}
int main(){
	int n=read<int>();
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=2;i<=n;++i) to[read<int>()].push_back(i);
	dfs(1);
	printf("%lld
",ans);
	return 0;
}

树上差分

https://www.luogu.com.cn/blog/dengyaotriangle/solution-p6623

看看就好。

CO int N=1<<21;
int a[N];
vector<int> to[N];
int w[21][N],f[N];

void dfs(int x,int d){
	f[x]=a[x];
	for(int i=0;i<=20;++i) f[x]^=w[i][d%(1<<i)];
	for(int y:to[x]) dfs(y,d+1),f[x]^=f[y];
	for(int i=0;i<=20;++i) f[x]^=w[i][d%(1<<i)];
	for(int i=0;i<=20;++i) w[i][(d+a[x])%(1<<i)]^=1<<i;
}
int main(){
	int n=read<int>();
	for(int i=1;i<=n;++i) read(a[i]);
	for(int i=2;i<=n;++i) to[read<int>()].push_back(i);
	dfs(1,0);
	printf("%lld
",accumulate(f+1,f+n+1,0LL));
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/13235181.html