联合省选2020 树
Posted autoint
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了联合省选2020 树相关的知识,希望对你有一定的参考价值。
树
给定一棵 (n) 个结点的有根树 (T),结点从 (1) 开始编号,根结点为 (1) 号结点,每个结点有一个正整数权值 (v_i)。
设 (x) 号结点的子树内(包含 (x) 自身)的所有结点编号为 (c_1, c_2, dots, 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;
}
以上是关于联合省选2020 树的主要内容,如果未能解决你的问题,请参考以下文章
loj 3301 「联合省选 2020 A」魔法商店 - 拟阵 - 保序回归