hdu 5416 CRB and Tree(异或运算)

本文详细介绍了HDU 5416问题的解题思路,包括官方题解、路径异或计算、最近公共祖先的应用及AC代码实现。通过DFS遍历树形结构并利用桶排序优化查询效率,最终实现复杂度为O(Q⋅N)的时间复杂度。

题目链接:

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;
}


### 2024 HDU运算 示例 教程 #### 什么是位运算? 位运算是指对二进制数中的每一位进行操作。常见的位运算符包括按位与 (`&`)、按位或 (`|`)、按位异或 (`^`)、左移 (`<<`) 和右移 (`>>`)。 #### 杭州电子科技大学(HDU)位运算题目解析 对于给定的代码片段: ```cpp for (int i = 1; i < (1 << n); i++) { if ((i >> 1) & i) continue; } ``` 这段代码用于遍历 `n` 个元素的所有子集,并过滤掉某些特定条件下的子集。具体解释如下: - `(1 << n)` 表示将数字 `1` 左移 `n` 位,相当于计算 \(2^n\) 的值[^3]。 - `i >> 1` 将变量 `i` 右移一位,相当于除以 2。 - `(i >> 1) & i` 判断当前子集中是否存在相邻位置都为 1 的情况。如果存在,则跳过该次循环。 #### 实际应用案例 假设有一个数组 `[a, b, c]`,即 `n=3`,则上述代码会生成并筛选这些子集: - 子集表示法:`{}` 对应于二进制 `000` - `{a}` 对应于二进制 `001` - `{b}` 对应于二进制 `010` - `{c}` 对应于二进制 `100` - `{a, b}` 对应于二进制 `011` (被过滤) - `{a, c}` 对应于二进制 `101` - `{b, c}` 对应于二进制 `110` (被过滤) - `{a, b, c}` 对应于二进制 `111` (被过滤) 通过这种方式,可以有效地排除那些不符合特定条件的组合。 #### Python 中的位运算示例 以下是类似的逻辑在 Python 中的实现方式: ```python def generate_subsets(n): result = [] for i in range(1, 1 << n): # 遍历所有可能的子集 if not (i & (i - 1)): # 过滤掉含有连续 '1' 的子集 subset = [j for j in range(n) if (i >> j) & 1] result.append(subset) return result print(generate_subsets(3)) ``` 此函数将会输出符合条件的子集列表。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值