查看: 19|回复: 0

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实...

[复制链接]
  • TA的每日心情
    无聊
    10 小时前
  • 签到天数: 44 天

    [LV.5]常住居民I

    43

    主题

    0

    回帖

    74

    积分

    注册会员

    Rank: 2

    积分
    74
    发表于 前天 15:29 | 显示全部楼层 |阅读模式
    一、题目解读
    洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在于高效计算任意两点路径的异或和,并找到最大值。
    二、解题思路
    采用“预处理+Trie树查询”策略:
        1. DFS预处理:通过深度优先搜索(DFS)遍历图,计算每个节点到根节点(默认为1)的路径权值异或和,存储于d数组
        2. Trie优化:将d数组中的每个值插入Trie树(二进制字典树),查询时利用Trie树特性,优先选择与当前位相反的子节点,以最大化异或结果。
        3. 贪心思想:每次查询时,从高位到低位遍历,若存在相反位分支,则选择该分支,确保异或值尽可能大。
    三、解题步骤
    1. 建图:使用邻接表存储无向图,边信息包含终点和权值。
    2. 预处理异或路径:从根节点开始DFS,递归计算每个节点到根的异或值d = d[父节点] ^ 边权值。
    3. 构建Trie树:将d数组元素逐位插入Trie树,每个节点维护0/1子节点指针
    4. 查询最大值:对每个d,在Trie树中从高位到低位遍历,若当前位相反分支存在,则转向该分支,累加贡献值,最终得到最大异或值。
    5. 更新答案:遍历所有节点的查询结果,取最大值输出。
    四、代码与注释
    1. #include <iostream>
    2. #include <vector>
    3. #include <algorithm>
    4. using namespace std;

    5. const int MAXN = 1e5 + 5; // 最大点数
    6. const int BIT = 30; // 二进制位数

    7. // 边结构
    8. struct Edge {
    9.     int to, w;
    10. };
    11. vector<Edge> G[MAXN]; // 邻接表

    12. int trie[MAXN * BIT][2], cnt = 1; // Trie树,cnt为节点计数器
    13. int d[MAXN]; // 节点到根的异或路径值

    14. // 预处理异或路径(DFS)
    15. void dfs(int u, int fa) {
    16.     for (auto &e : G[u]) { // 遍历u的所有邻接边
    17.         if (e.to == fa) continue; // 跳过父节点
    18.         d[e.to] = d[u] ^ e.w; // 更新子节点的异或值
    19.         dfs(e.to, u); // 递归处理子树
    20.     }
    21. }

    22. // 向Trie树插入数字x
    23. void insert(int x) {
    24.     int p = 1; // 从根节点开始
    25.     for (int i = BIT; i >= 0; i--) { // 从最高位到最低位
    26.         int b = (x >> i) & 1; // 获取当前位
    27.         if (!trie[p][b]) trie[p][b] = ++cnt; // 若子节点不存在,创建新节点
    28.         p = trie[p][b]; // 移动到子节点
    29.     }
    30. }

    31. // 查询与x异或最大的值
    32. int query(int x) {
    33.     int p = 1, res = 0; // 从根节点开始,结果初始化
    34.     for (int i = BIT; i >= 0; i--) {
    35.         int b = (x >> i) & 1; // 当前位
    36.         if (trie[p][!b]) { // 若相反位分支存在
    37.             res += (1 << i); // 累加贡献值
    38.             p = trie[p][!b]; // 转向相反分支
    39.         } else {
    40.             p = trie[p][b]; // 否则沿原分支
    41.         }
    42.     }
    43.     return res;
    44. }

    45. int main() {
    46.     int n;
    47.     cin >> n; // 输入点数
    48.     for (int i = 1; i < n; i++) { // 输入n-1条边
    49.         int u, v, w;
    50.         cin >> u >> v >> w;
    51.         G[u].push_back({v, w}); // 双向建边
    52.         G[v].push_back({u, w});
    53.     }
    54.    
    55.     dfs(1, 0); // 从根节点1开始预处理

    56.     int ans = 0;
    57.     for (int i = 1; i <= n; i++) {
    58.         insert(d[i]); // 插入到Trie树
    59.         ans = max(ans, query(d[i])); // 更新最大异或值
    60.     }
    61.    
    62.     cout << ans << endl; // 输出结果
    63.     return 0;
    64. }
    复制代码


    五、总结
    本解法巧妙结合图论预处理与Trie树查询,将路径异或问题转化为二进制位匹配问题。通过预处理计算节点到根的异或值,利用Trie树高效查找最大异或值,时间复杂度为O(nlogn)。该算法对处理大规模异或路径问题具有借鉴意义,尤其适用于需要快速查找异或极值的场景。
    来源:自学信奥

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    快速回复 返回顶部 返回列表