CSU - 2082 I'm new here(树上路径异或和)

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 u=1nv=u+1nD(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个节点的树,有n-1条边,每条边上有边权,求该树上一个节点到任意节点的路径异或和之加和。

深搜算出根节点(任意一个)到达每个节点路径的异或和。然后用到结论,树上任意两点的路径异或和等于两点到达根节点异或和的异或值。然后我们要计算的是所有路径异或和的加和,那么取一个节点到达剩下所有节点的路径异或和中二进制位上不同的值,才是加和里有效的。即,1与1异或和0与0异或都是0,在求和时不体现这一位。而1和0的组合才会有值存在,才会加到最终的sum中。

也就是说对于一个节点到根节点的路径异或和的二进制,每一位上记录1出现的个数cnt。而剩下的n-cnt个节点的路径上这一位是0,那么这n-cnt个节点到达cnt个节点的路径异或值这一位为1,在sum中加上这一位的十进制值。就(n-cnt)*cnt种路径组合。

最后对二进制位上每一位计算组合数量,乘该二进制位的十进制值求和,即最终结果

#include<stdio.h>///树上任意两点的异或和,可根据两点到根节点的异或和 异或得到
#include<string.h>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
const int maxn=100005;
struct edge
{
    int to,val;
    edge(){}
    edge(int a,int b)
    {
        to=a;
        val=b;
    }
};
int n,t;
int xorsum[maxn],cnt[40];
vector<edge>q[maxn];
void dfs(int rt,int pre)
{
    for(int i=0;i<q[rt].size();i++)
    {
        edge tmp=q[rt][i];
        if(tmp.to!=pre)
        {
            xorsum[tmp.to]=xorsum[rt]^tmp.val;///根节点到每个节点的异或和
            dfs(tmp.to,rt);
        }
    }
    for(int i=0;i<31;i++)if(xorsum[rt]&(1<<i))cnt[i+1]++;///记录每个异或和的二进制位上1出现的次数
}
LL binary[40];
int main()
{
    for(int i=0;i<31;i++)binary[i+1]=1<<i;
    scanf("%d",&t);
    while(t--)
    {
        memset(cnt,0,sizeof cnt);
        int from,to,val;
        scanf("%d",&n);
        for(int i=0;i<=n;i++)q[i].clear();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d%d",&from,&to,&val);
            q[from].push_back(edge(to,val));
            q[to].push_back(edge(from,val));
        }
        xorsum[1]=0;
        dfs(1,0);
        LL ans=0;
        for(int i=1;i<=31;i++)ans+=binary[i]*(n-cnt[i])*cnt[i];
        printf("%lld\n",ans);
    }
}
///得到根节点到每个节点位置的异或和后,那么计算任意两点的异或和只需要将任意两路径异或和进行异或操作,又因为我们要计算的是所有路径异或和的加和
///因此任意两结点路径异或之后,只取二进制位上位1的值计算,那么只有01进行异或才会在二进制位上得到1,直接用所有10匹配,得到异或出1的情况数量,转换为十进制求和。
posted @ 2018-06-23 20:17  KuroNekonano  阅读(131)  评论(0编辑  收藏  举报