P6623 [省选联考 2020 A 卷] 树 题解

P6623 [省选联考 2020 A 卷] 树

题面:

题目描述

给定一棵 n n n 个结点的有根树 T T T,结点从 1 1 1 开始编号,根结点为 1 1 1 号结点,每个结点有一个正整数权值 v i v_i vi

x x x 号结点的子树内(包含 x x x 自身)的所有结点编号为 c 1 , c 2 , … , c k c_1,c_2,\dots,c_k c1,c2,,ck,定义 x x 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 ) d(x,y) d(x,y) 表示树上 x x x 号结点与 y y y 号结点间唯一简单路径所包含的边数, d ( x , x ) = 0 d(x, x) = 0 d(x,x)=0 ⊕ \oplus 表示异或运算。

请你求出 ∑ i = 1 n v a l ( i ) \sum\limits_{i=1}^n val(i) i=1nval(i) 的结果。

输入格式

第一行一个正整数 n n n 表示树的大小。

第二行 n n n 个正整数表示 v i v_i vi

接下来一行 n − 1 n-1 n1 个正整数,依次表示 2 2 2 号结点到 n n n 号结点,每个结点的父亲编号 p i p_i pi

输出格式

仅一行一个整数表示答案。

样例 #1
样例输入 #1
5
5 4 1 2 3
1 1 2 2
样例输出 #1
12
提示

【样例解释 1 1 1

v a l ( 1 ) = ( 5 + 0 ) ⊕ ( 4 + 1 ) ⊕ ( 1 + 1 ) ⊕ ( 2 + 2 ) ⊕ ( 3 + 2 ) = 3 val(1)=(5+0)\oplus(4+1)\oplus(1+1)\oplus(2+2)\oplus(3+2)=3 val(1)=(5+0)(4+1)(1+1)(2+2)(3+2)=3

v a l ( 2 ) = ( 4 + 0 ) ⊕ ( 2 + 1 ) ⊕ ( 3 + 1 ) = 3 val(2)=(4+0)\oplus(2+1)\oplus(3+1) = 3 val(2)=(4+0)(2+1)(3+1)=3

v a l ( 3 ) = ( 1 + 0 ) = 1 val(3)=(1+0)=1 val(3)=(1+0)=1

v a l ( 4 ) = ( 2 + 0 ) = 2 val(4)=(2+0)=2 val(4)=(2+0)=2

v a l ( 5 ) = ( 3 + 0 ) = 3 val(5)=(3+0)=3 val(5)=(3+0)=3

和为 12 12 12

【数据范围】

对于 10 % 10\% 10% 的数据: 1 ≤ n ≤ 2501 1\leq n\leq 2501 1n2501

对于 40 % 40\% 40% 的数据: 1 ≤ n ≤ 152501 1\leq n\leq 152501 1n152501

另有 20 % 20\% 20% 的数据:所有 p i = i − 1 p_i=i-1 pi=i1 2 ≤ i ≤ n 2\leq i\leq n 2in);

另有 20 % 20\% 20% 的数据:所有 v i = 1 v_i=1 vi=1 1 ≤ i ≤ n 1\leq i\leq n 1in);

对于 100 % 100\% 100% 的数据: 1 ≤ n , v i ≤ 525010 1\leq n,v_i \leq 525010 1n,vi525010 1 ≤ p i ≤ n 1\leq p_i\leq n 1pin

01-trie 的 合并

考虑整体加 1 1 1,在 01-trie 上模拟进位。

整体加 1 1 1 就是在交换 0 , 1 0,1 0,1 两边的树,然后往进位的方向继续进行交换操作。

求解的时候,对 Trie 上每个点分别计算贡献。

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}
void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
const int N = 525015;
int v[N],head[N],nxt[N<<1],to[N<<1],cnt;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v) {
	nxt[cnt] = head[u];
	to[cnt] = v;
	head[u] = cnt++;
}
int rt[N];

namespace sgt{
int ls[N * 30],rs[N * 30];
int t[N * 30],cnt,s[N * 30];

void push_up(int p,int dep) {
	s[p] = s[ls[p]] + s[rs[p]];
	t[p] = t[ls[p]] ^ t[rs[p]] ^ ((s[rs[p]] & 1) << dep);
}

void insert(int &p,int x,int dep) {
	if(!p) p = ++cnt;
	if(dep >= 22) {t[p] = 0;s[p]++;return;}
	(x >> dep) & 1 ? insert(rs[p],x,dep + 1) : insert(ls[p],x,dep + 1);
	push_up(p,dep);
}

void reverse(int p,int dep) {
	if(dep >= 22) return;
	reverse(rs[p],dep + 1);
	swap(ls[p],rs[p]);
	push_up(p,dep);
}

int merge(int x,int y,int dep) {
	if(!x || !y) return x + y;
	if(dep >= 22) {
		s[x] += s[y];
		return x;
	}
	ls[x] = merge(ls[x],ls[y],dep + 1);
	rs[x] = merge(rs[x],rs[y],dep + 1);
	push_up(x,dep);
	return x;
}

int query(int p) {return t[p];}
}

signed main() {
	init();
	int n = rd();
	for(int i = 1;i<=n;i++)	
		v[i] = rd();
	for(int i = 2,k;i<=n;i++) 
		add(i,k = rd()),add(k,i);
	int ans = 0;
	auto dfs = [&](auto self,int x,int f) -> void {
		for(int i = head[x];~i;i = nxt[i]) {
			int y = to[i];
			if(y ^ f){
				self(self,y,x);
				rt[x] = sgt::merge(rt[x],rt[y],0);
			}
		}
		sgt::reverse(rt[x],0);
		sgt::insert(rt[x],v[x],0);
		ans += sgt::query(rt[x]);
	};
	dfs(dfs,1,0);
	wt(ans);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值