中南大学第十二届大学生程序设计竞赛 D树上异或性质

题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=2082


Description

The environment of Beijing is insufferable as you know.You have to wear a mask when you go outside because of the smog. Xiaoming is new here,he has to get accustomed to the lifestyle of beijing.So at the first day he arrived there,he decide to go around to see what the whole city like.He choose n destination.But you know,the road in beijing is complex,so a thought come into xiaoming's mind :why not use "MST" algorithm to link all destination?So after a few minute,xiaoming get a map with n destination and n-1 road between them. We can define Path(u,v) as the path in the tree that doesn't pass a node twice between node u and v.What's more,if we define E(u,v) is the set of edge that appear in Path(u,v),the distance between node u and v,we can define it as D(u,v), will be equal to the sum of exclusive-or of these edge. Xiaoming want to know nu=1nv=u+1D(u,v)

,can you help him?

Input

Input begins with a line with one integer T (1 ≤ T ≤ 5) denoting the number of test cases. Each test case begins with a line a integers n, where n (1 ≤ n ≤ 100000) denotes the number of destination. Next follow n-1 lines with three space-separated integers u, v, and w, which means there exists a edge between node u and v,and the weight of it is w(w < 2^31)

Output

For each test case, print a single line containing the answer.

Sample Input

2
4
1 2 1
2 3 1
2 4 2
4
1 2 3
2 3 1
2 4 2

Sample Output

10
12

题意:    给定一棵树(N<1e5),树边上有边权,现在求任意点对的路径异或值的和。

题解: 因为两点之间的异或Xor(u,v)=Xor(root,u)^Xor(root,v)。所以我们DFS得到每个点到根的异或值Xi。然后计算两两异或值。因为异或没有结合性,所以不能用前缀和来计算。正确的打开方式是对每一位sig,我们计算1的个数(cnt)和0的个数(N-cnt),那么这一位的贡献就是cnt*(N-cnt)*(1<<sig)。然后累计即可。

代码:

#include <stdio.h>
#include <algorithm>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <set>
#include <iostream>

using namespace std;
const int maxn = 1e5 + 7;
typedef long long ll;
vector<pair<int,int> > g[maxn];
int d[maxn];

void dfs(int u, int fa, int sta)
{
    d[u] = sta;
    for(int i = 0;i < g[u].size();i ++) {
        pair<int,int> it = g[u][i];
        if(it.first != fa) {
            dfs(it.first, u, sta ^ it.second);
        }
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    while(T --) {
        int n;
        scanf("%d", &n);
        for(int i = 1;i <= n;i ++) {
            g[i].clear();
            d[i] = 0;
        }
        for(int i = 1;i < n;i ++) {
            int u, v, w;
            scanf("%d %d %d",&u, &v, &w);
            g[u].push_back(make_pair(v, w));
            g[v].push_back(make_pair(u, w));
        }
        dfs(1, 0, 0);
        ll res = 0;
        for(int j = 0;j < 31;j ++) {
            int bit0 = 0, bit1 = 0;
            for(int i = 1;i <= n;i ++) {
                if( (d[i] >> j) & 1 ) {
                    bit1 ++;
                } else {
                    bit0 ++;
                }
            }
            res += (1LL << j) * bit1 * 1LL * bit0;
        }
        printf("%lld\n", res);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值