题解:洛谷 P4551 最长异或路径

【题目来源】

洛谷:P4551 最长异或路径 - 洛谷

【题目描述】

给定一棵 \(n\) 个点的带权树,结点下标从 \(1\) 开始到 \(n\)。寻找树中找两个结点,求最长的异或路径。

异或路径指的是指两个结点之间唯一路径上的所有边权的异或。

【输入】

第一行一个整数 \(n\),表示点数。

接下来 \(n−1\) 行,给出 \(u,v,w\),分别表示树上的 \(u\) 点和 \(v\) 点有连边,边的权值是 \(w\)

【输出】

一行,一个整数表示答案。

【输入样例】

4
1 2 3
2 3 4
2 4 6

【输出样例】

7

【解题思路】

image

【算法标签】

《洛谷 P4551 最长异或路径》 #数学# #贪心# #字典树,Trie#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// 字典树节点结构体
struct node 
{
    int left = 0;   // 左子节点(存储0)
    int right = 0;  // 右子节点(存储1)
} root;             // 根节点

vector<node> trie;              // 字典树存储结构
vector<int> a[100005], b[100005]; // a: 邻接表, b: 边权值
int data[100005];               // 存储每个节点的异或值
int n, x, y, z, ans;            // n: 节点数, x/y/z: 临时变量, ans: 最终结果

/**
 * 深度优先搜索计算每个节点的异或值
 * @param x 当前节点
 * @param y 当前异或值
 */
void dfs(int x, int y)
{
    for (int i = 0; i < a[x].size(); i++) 
    {
        // 计算子节点的异或值(当前异或值 ^ 边权值)
        data[a[x][i]] = y ^ b[x][i];
        // 递归处理子节点
        dfs(a[x][i], data[a[x][i]]);
    }
}

/**
 * 构建字典树
 * @param x 要插入的数值
 */
void build(int x)
{
    int fa = 0;     // 从根节点开始
    int len;        // 新节点位置
    
    // 从最高位到最低位处理
    for (int i = (1 << 30); i > 0; i >>= 1) 
    {
        len = trie.size();
        if (x & i)  // 当前位为1
        {
            if (trie[fa].right == 0)  // 右子节点不存在
            {
                trie[fa].right = len; // 创建右子节点
                trie.push_back(root);
                fa = len;
            } 
            else 
            {
                fa = trie[fa].right;  // 移动到右子节点
            }
        } 
        else        // 当前位为0
        {
            if (trie[fa].left == 0)   // 左子节点不存在
            {
                trie[fa].left = len;  // 创建左子节点
                trie.push_back(root);
                fa = len;
            } 
            else 
            {
                fa = trie[fa].left;   // 移动到左子节点
            }
        }
    }
}

/**
 * 查询字典树,找到与x异或最大的值
 * @param x 要查询的数值
 * @return 最大异或值
 */
int query(int x)
{
    int ans = 0;    // 存储结果
    int fa = 0;     // 从根节点开始
    
    // 从最高位到最低位处理
    for (int i = (1 << 30); i > 0; i >>= 1) 
    {
        int a = x & i;  // 获取当前位
        if (a)          // 当前位为1
        {
            // 优先选择0(左子节点)以获得更大的异或结果
            if (trie[fa].left) 
            {
                ans += i;       // 累加结果
                fa = trie[fa].left;
            } 
            else 
            {
                fa = trie[fa].right;
            }
        } 
        else            // 当前位为0
        {
            // 优先选择1(右子节点)以获得更大的异或结果
            if (trie[fa].right) 
            {
                ans += i;       // 累加结果
                fa = trie[fa].right;
            } 
            else 
            {
                fa = trie[fa].left;
            }
        }
    }
    return ans;
}

int main()
{
    // 输入节点数
    cin >> n;
    
    // 输入树结构(n-1条边)
    for (int i = 0; i < n - 1; i++) 
    {
        cin >> x >> y >> z;
        a[x].push_back(y);  // 构建邻接表
        b[x].push_back(z);  // 存储边权值
    }
    
    // 计算每个节点的异或值(从根节点1开始)
    dfs(1, 0);
    
    // 初始化字典树(添加根节点)
    trie.push_back(root);
    
    // 将所有节点的异或值插入字典树
    for (int i = 1; i <= n; i++) 
    {
        build(data[i]);
    }
    
    // 查询每个节点的最大异或值,更新最终结果
    for (int i = 1; i <= n; i++) 
    {
        ans = max(ans, query(data[i]));
    }
    
    // 输出结果
    cout << ans << endl;
    
    return 0;
}

【运行结果】

4
1 2 3
2 3 4
2 4 6
7
posted @ 2026-02-19 14:23  团爸讲算法  阅读(1)  评论(0)    收藏  举报