P6623 [省选联考 2020 A 卷] 树

P6623 [省选联考 2020 A 卷] 树

题目描述

给定一棵 \(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)\) 的结果。

输入格式

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

第二行 \(n\) 个正整数表示 \(v_i\)

接下来一行 \(n-1\) 个正整数,依次表示 \(2\) 号结点到 \(n\) 号结点,每个结点的父亲编号 \(p_i\)

输出格式

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

【数据范围】

对于 \(100\%\) 的数据:\(1\leq n,v_i \leq 525010\)\(1\leq p_i\leq n\)

Solution:

好神的二进制题。

我们发现从祖先子树是不明智的,所以我们考虑刻画单点对于祖先的影响:

K val
0 001001
1 001010
2 001011
3 001100
4 001101
5 001110
6 001111
7 010000
8 010001
9 010010
10 010011
bit 0 1 2 3 4 5 6 7 8 9 10
0 1 0 1 0 1 0 1 0 1 0 1
1 0 1 1 0 0 1 1 0 0 1 1
2 0 0 0 1 1 1 1 0 0 0 0
3 1 1 1 1 1 1 0 0 0 0 0

打完这张表之后我们发现随着相对深度的上涨,一个点对其 \(k\) 级祖先在二进制下的第 \(bit\) 位的贡献是有规律的,是一个长度为 \(2^{bit+1}\) 的循环节,其中前半部分是 0 ,后半部分是 1。

那么一种较为朴素的想法是从当前节点 \(pos\) 向上跳,将其 \(k\) 级祖先的每个点的每个二进制位都按照上表对应1的异或一下,但是这样是 \(O(n^2logV)\) 的。或许你能想到树链剖分一下,但是那样也只是\(O(nlog^2n\times logV)\)

假设现在我们只考虑 \(bit\) 位上的贡献:
我们考虑利用一下循环节这一特性,我们可以打一个差分,来维护那些点需要异或上1,然后我们就发现,对于一个点 \(pos\) 他能贡献到的所有节点的深度对 \(2^{bit}\) 取模之后是同余的。所以我们可以维护一个桶 \(w[j][dep]\),用来表示在第 \(j\) 位上,深度对 \(2^{bit}\) 取模后为 \(dep\) 的点的答案应该异或上 \(w[j][dep]\)

那么如何保证子树内节点只对祖先贡献呢?很简单,我们对 \(w\) 再差分一下就好了,记录一下进入每个节点时桶的状态 \(ans\)。退出这个节点时再拿退出时的桶 \(w\)\(ans\) 异或取到真正的答案。

Code:

#include<bits/stdc++.h>
#define int long long
const int N=6e5+5;
const int lg=20;
using namespace std;
int bit[lg+5],full_bit[lg+5];
int w[lg+5][N],val[N];
vector<int> E[N];
int n,sum;

void init()
{

    for(int i=0;i<=lg;i++)bit[i]=(1ll<<i);
    for(int i=0;i<=lg;i++)full_bit[i]=bit[i]-1;
}
int dfs(int x,int dep)
{
    int ans=val[x];
    for(int j=0;j<=lg;j++)w[j][(dep+val[x])&(full_bit[j])]^=bit[j];//&full_bit :对2^j 取模
    for(int j=0;j<=lg;j++)ans^=w[j][dep&(full_bit[j])];
    for(auto y : E[x])ans^=dfs(y,dep+1);
    for(int j=0;j<=lg;j++)ans^=w[j][dep&(full_bit[j])];
    sum+=ans;
    return ans;
}
void work()
{
    cin>>n;
    init();
    for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
    for(int u=2,v;u<=n;u++)scanf("%lld",&v),E[v].emplace_back(u);
    dfs(1,0);
    cout<<sum;
}
#undef int
int main()
{
    //freopen("P6623.in","r",stdin);freopen("P6623.out","w",stdout);
    work();
    return 0;
}
posted @ 2025-02-19 14:19  liuboom  阅读(25)  评论(0)    收藏  举报