牛客练习赛32 B题:Xor Path 树上结点贡献次数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiang_6/article/details/84678473

题意:

给定一颗n个结点的树(n-5e5),每个结点有个权值ai(ai-1e9)

现在定义一个值 path[i][j] 表示i结点到j结点最短路径上 经过的所有结点权值的异或和

现在要求计算所有的 path[i][j] 异或和,i 范围1~n-1, j 范围 i+1 ~ n

思路:

首先树上最短路径是唯一的,即两个点只有一条路径(每个点只能走一次的话);

然后考虑到计算所有path值的异或和的时候,我们会发现,最后的答案就是每跳路径中经过的点的权值的异或和

然后我们可以算每个结点在所有路径中出现的次数,也就是算他的贡献次数,

至于这个点贡献了多少次,可以分为两部分:一部分是在其他两点路径上,另一部分是这个点作为path的开头

第一部分的值就是这个点连接的每个点及后面的点看作子树,和另外的所有的点算一下,相当于贡献次数,每个结点都算

第二部分都是n-1次;

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;

const ll mod = 1e9 + 7;
const ll maxn = 1e6 + 7;


ll n;
vector<ll> vec[maxn];
ll a[maxn];
ll ans = 0;

ll dfs(ll id, ll f) {
    ll sum = 0;
    ll t = 0;
    ll res = 1; // 子树大小
    for(auto i : vec[id]) {
        if(i == f) continue;
        t = dfs(i, id);
        res += t;
        sum += (t*(n-t-1)); //sum %= 2LL;
    }
    sum += (n-res)*(res-1); sum /= 2LL;//sum %= 2LL;
    sum += (n-1); sum %= 2LL;
    if(sum&1) ans ^= a[id];
    return res;
}

int main() {
    scanf("%lld", &n);
    for(int i = 1; i < n; ++i) {
        ll x, y; scanf("%lld%lld", &x, &y);
        vec[x].push_back(y);
        vec[y].push_back(x);
    }
    for(int i = 1; i <= n; ++i) {
        scanf("%lld", &a[i]);
    }

    int x = dfs(1, -1);
    printf("%lld", ans);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiang_6/article/details/84678473