ICPC NEAU Programming Contest 2020 M 再来异或 【树+边权的贡献】

Description  

给你具有n个结点n−1条边的无向无环连通图,结点编号1∼n,每条边上有一个数作为他的边权,定义函数f(i,j)为连接i,j的简单路径的所有边权的异或值

\bigoplus_{i=1}^n\bigoplus_{j=i}^nf(i,j),⊕为按位异或运算,\bigoplus _{i=l}^ri表示l∼r所有整数异或后的结果

Input 

输入的第一行为一个整数T,代表测试用例的组数

接下来的T组测试用例按照如下格式给出:

每组数据占n行,第一行有1个整数n,接下来的n−1行,每行有3个整数u,v,w,分别表示每条边的起点、终点、权值

Output

对于每组测试数据,在新的一行中输出答案 

数据范围

1≤T≤1000

1≤n≤2⋅10^5

∑n≤2⋅10^5

1≤u,v≤n

0≤w≤10^6

样例输入

2
1
2
1 2 3 

样例输出

0

题目大意:

求树中所有结点两两组合的函数值的异或和,其中,函数f(i,j)为连接i,j的简单路径的所有边权的异或值。

分析:

考虑每条边权在总的答案中的贡献。

对于一条边 (u,v) ,边权为 w ,由于树的性质,则所有在 u 一侧的点要到达在 v 一侧的点都必须经过这条边,假设在 u 一侧的点(包括 u )有 sz[u] 个,在 v 一侧的点(包括 v)有 sz[v] 个,则这条边在答案中计算了 sz[u] * sz[v] 次。

由于异或的性质,如果 sz[u] * sz[v] 为偶数,则这条边对答案的贡献为0;如果 sz[u] * sz[v] 为奇数,则这条边对答案的贡献为w。

具体解释见代码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define mst(a,num) memset(a,num,sizeof a)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef pair PII;
typedef pair PLL;
typedef vector VI;
const ll mod = 1e9 + 7;
const int maxn = 200000 + 5;

struct node
{
    int v,w;
};


vector edge[maxn];
int ans=0;
int sz[maxn];
int n;

void dfs(int x,int fa){
    sz[x]=1;
    for(auto &nod:edge[x]){
        if(nod.v==fa)  continue;
        dfs(nod.v,x);
        sz[x]+=sz[nod.v];
        int f1=(sz[nod.v]&1),f2=((n-sz[nod.v])&1);
        if(f1==1&&f2==1){
            ans^=nod.w;
        }
    }
}

int main() {
    int t;
    scanf("%d",&t);
    while(t--){
        int u,v,w;
        scanf("%d",&n);
        rep(i,1,n)  edge[i].clear();
        node tmp;
        rep(i,1,n-1){
            scanf("%d%d%d",&u,&v,&w);
            tmp.w=w;
            tmp.v=v;
            edge[u].push_back(tmp);
            tmp.v=u;
            edge[v].push_back(tmp);
        }
        ans=0;
        dfs(1,0);
        printf("%d\n",ans);
    }
    return 0;
}

 

你可能感兴趣的:(ACM学习,图论)