HDU 5416 CRB and Tree (树形DP)

题目链接:传送门 

题意:

在一棵树上,给定一个函数F(u,v)表示从u到v的路径上的边的权值异或起来的结果,然后求F(u,v)=s有多少种情况。


分析:

设dp[u]表示点u到根节点的路径异或起来的值,然后F(u,v)=dp[u]^dp[v],我们设cnt[x]表示dp[u]=x的情况有多少种,那么在s不等于0的情况时ans[s] = sigma(cnt[i]*cnt[s^i]),注意s = 0 的情况。


代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;

const int maxn = 1e6+10;

typedef long long LL;

int dp[maxn];

int head[maxn],ip;

LL cnt[maxn];

int n;

struct nod{
    int to,next,w;
}edge[maxn];

bool vis[maxn];
void init(){
    ip=0;
    memset(head,-1,sizeof(head));
    memset(cnt,0,sizeof(cnt));
    memset(vis,0,sizeof(vis));
}

void add(int u,int v,int w){
    edge[ip].to=v;
    edge[ip].w=w;
    edge[ip].next=head[u];
    head[u]=ip++;
}

int mmax;


void dfs(int u,int pre,int val){
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v= edge[i].to;
        if(v==pre) continue;
        if(edge[i].w^val>mmax)
            mmax = edge[i].w^val;
        cnt[edge[i].w^val]++;
        dfs(v,u,val^edge[i].w);
    }
}

int main()
{
    int t,m;
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%d",&n);
        for(int i=0;i<n-1;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        mmax = 0;
        dfs(1,1,0);
        scanf("%d",&m);
        cnt[0]++;
        for(int i=0;i<m;i++){
            int s;
            scanf("%d",&s);
            LL ans = 0;
            for(int j=0;j<maxn;j++){
                if((s^j)==j) ans=ans+(cnt[j]*(cnt[j]-1));
                else ans = ans=ans+cnt[j]*cnt[s^j];
            }
            ans=ans/2;
            if(s==0) ans=ans+n;
            printf("%I64d\n",ans);
        }
    }
    return 0;
}
/***
45
7
1 2 1
1 3 1
1 4 1
4 5 2
3 6 2
2 7 2
1123
1
2
3
0
**/


你可能感兴趣的:(HDU 5416 CRB and Tree (树形DP))