hdu 5416 CRB and Tree(异或运算)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5416

解题思路:

官方题解:

We can easily proved that f(u, v)\f(u,v) =\ f(1, u) f(1,u) ^f(1, v)f(1,v).

Traversing tree from vertex 1 as the root, all values f(1, u)f(1,u) can be calculated in linear time. These values must be put into buckets.

For each query ssf(u, v)\ =\ s\Leftrightarrow f(1, v)\ =\ f(1, u)f(u,v) = sf(1,v) = f(1,u)ss indicates that for each vertex uu, number of vertices vv satisfies that f(u, v)\ =\ sf(u,v) = s can be calculated from the buckets in constant time. (Note that some exceptions must be processed such as s\ =\ 0s = 0 or u\ =\ vu = v)

Time complexity:O(Q\cdot N)O(QN)

设路径{u,v}的边权异或结果为 f(u,v)

设lca 为u v的最近公共祖先

首先得到一个结论,f(u,v) =f(lca, u) ^ f(lca, v)

因为f(lca, root) ^ f(lca, root) == 0

所以 f(u,v) =( f(lca,u)^f(lca,root) ) ^ ( f(lca, v) ^ f(lca, root)) = f(root,u) ^ f(root, v)

然后用一个数组存下从根到任意一个点的路径异或值。

最后对每个询问,枚举以某个点为端点的路径个数即可。

因为这样算出来的path(u,v)是计算了2遍的,所以结果要/2

注意一下0的情况即可。

AC代码:

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

typedef long long ll;
const int N = 100005;
const int maxn = N<<1;
struct node{
    int x,w;
};
vector<node> edge[N];
int vis[N];
ll cnt[maxn];

void dfs(int u,int val){
    vis[u] = 1;
    if(u^1)
        cnt[val]++;
    int l = edge[u].size();
    for(int i = 0; i < l; i++){
        node tmp = edge[u][i];
        if(!vis[tmp.x]){
            dfs(tmp.x,tmp.w^val);
        }
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n,q;
        scanf("%d",&n);
        for(int i = 1; i <= n; i++)
            edge[i].clear();
        int u,v,w;
        for(int i = 1; i < n; i++){
            scanf("%d%d%d",&u,&v,&w);
            edge[u].push_back(node{v,w});
            edge[v].push_back(node{u,w});
        }
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        dfs(1,0);

        scanf("%d",&q);
        while(q--){
            scanf("%d",&w);
            ll ans = 0;
            if(w == 0){
                ans += n+cnt[0];
                for(int i = 0; i < maxn; i++)
                    ans += cnt[i] * (cnt[i] - 1) / 2;
            }
            else{
                for(int i = 0; i < maxn; i++)
                    ans += cnt[i] * (cnt[i ^ w]);
                ans >>= 1;
                ans += cnt[w];
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值