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=1∑nval(i) 的结果。
输入格式
第一行一个正整数 n n n 表示树的大小。
第二行 n n n 个正整数表示 v i v_i vi。
接下来一行 n − 1 n-1 n−1 个正整数,依次表示 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 1≤n≤2501;
对于 40 % 40\% 40% 的数据: 1 ≤ n ≤ 152501 1\leq n\leq 152501 1≤n≤152501;
另有 20 % 20\% 20% 的数据:所有 p i = i − 1 p_i=i-1 pi=i−1( 2 ≤ i ≤ n 2\leq i\leq n 2≤i≤n);
另有 20 % 20\% 20% 的数据:所有 v i = 1 v_i=1 vi=1( 1 ≤ i ≤ n 1\leq i\leq n 1≤i≤n);
对于 100 % 100\% 100% 的数据: 1 ≤ n , v i ≤ 525010 1\leq n,v_i \leq 525010 1≤n,vi≤525010, 1 ≤ p i ≤ n 1\leq p_i\leq n 1≤pi≤n。
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;
}