一颗N个点的树,求每个点到其他所有点的路径长度异或K的总和

题意:

一颗N个点的树,求每个点到其他所有点的路径长度异或K的总和。

即ANSi=sum(distance(i,j) xor K){1<=j<=N, j!=i}

Input

第一行一个整数t,测试数据组数
每组数据第一行两个整数N,K 如题述。(N<=10^5,K<16)
然后N-1 行每行三个整数a,b,c 表示a 到b 有一条长为c 的边。

Output

对于每组数据输出N行。
第i 行为ANSi。

Sample Input

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

Sample Output

6
8
10
12


首先我们假设没有异或,那么先算出其他所有点到1的距离,然后已知所有点到他的父亲的距离,可以推知所有点到这个点的距离

比如,sum[fa]表示所有点到这个点父亲的距离的和,sum1[v]表示他的子孙到他的距离和,设他和他的父亲之间的距离为x,他的子孙

数目为sz[v],]那么所有点到他的距离和便为sum[v]+(sum[fa]-sum1[v])+(n-sz[v])*x

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
typedef long long ll;
typedef pair<ll,ll>pi;
vector<pi>G[maxn];
ll sz[maxn];
ll dp[maxn][17];
ll sum1[maxn],sum[maxn];
ll rep[maxn];
int w,n;

void dfs1(int u,int p){
    sz[u]=1;
    sum[u]=0;
    for(int i=1;i<16;i++)
        dp[u][i]=0;
    dp[u][0]=1;
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i].first;
        int k=G[u][i].second;
        if(v==p)
            continue;
        dfs1(v,u);
        sum1[v]=0;
        int x=k/16,y=k%16;
        sz[u]+=sz[v];
        sum[u]+=sum[v]+sz[v]*16*x;         //sum[]一定为16的倍数
        sum1[v]+=sum[v]+sz[v]*16*x;         //sum[v]-sum1[v]即为不是他的子孙到他的距离
        for(int j=0;j<16;j++){
            dp[u][(j+y)%16]+=dp[v][j];
            if(j+y>=16){
                sum[u]+=dp[v][j]*16;
                sum1[v]+=dp[v][j]*16;
            }
        }
    }
}

void dfs2(int u,int p){
    rep[u]=sum[u];
    //printf("u is %d sum[u] is %I64d\n",u,sum[u]);
    for(int i=0;i<16;i++){
        //if(u==1){
            //printf("%d\n",dp[u][i]);
        //}
        rep[u]+=dp[u][i]*(i^w);
    }
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i].first;
        int k=G[u][i].second;
        if(v==p)
            continue;
        int x=k/16,y=k%16;
        sum[v]+=(sum[u]-sum1[v]);
        sum[v]+=(n-sz[v])*16*x;
        vector<int>V;
        V.assign(17,0);
        for(int j=0;j<16;j++){
            V[(j+y)%16]+=dp[u][j]-dp[v][(j-y+16)%16];
            if(j+y>=16){
                sum[v]+=(dp[u][j]-dp[v][(j-y+16)%16])*16;
            }
        }
        for(int j=0;j<16;j++)
            dp[v][j]+=V[j];
        dfs2(v,u);
    }
}

void init(int n){
    for(int i=1;i<=n;i++)
        G[i].clear();
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&w);
        init(n);
        int u,v,k;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&k);
            G[u].push_back(pi(v,k));
            G[v].push_back(pi(u,k));
        }
        dfs1(1,0);
        dfs2(1,0);
        for(int i=1;i<=n;i++)
            printf("%I64d\n",rep[i]-w);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值