The xor-longest Path POJ - 3764(树上异或最长路径+trie)

本文介绍了一种利用Trie树优化树形DP算法解决路径异或和最大值问题的方法。通过构建Trie树并从高位到低位遍历,可以在O(n*31)的时间复杂度内找到树上任意两点间路径的最大异或和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:传送门
题解:给出一棵树,求解树上一条路径异或和最大,首先考虑跑一个有根树, d [ i ] d[i] d[i]表示从 i i i到根的异或和,之后比如 i i i j j j的路径异或和就是 d [ i ] ∧ d [ j ] d[i]\wedge d[j] d[i]d[j],这点可以推算得知,从一堆数中选出两个数异或和最大,暴力 O ( n 2 ) O(n^2) O(n2),但是如果优化下,从第 31 31 31位开始想,如果为 1 1 1,那么从剩下数中看第 31 31 31位有是 0 0 0的么,那么再从这些依次选取,否则就只好选 1 1 1的那部分,之后方法类似,可以把这些数都建成 t r i e trie trie树,然后从 31 31 31位到 1 1 1位跑,复杂度 O ( n ) ∗ 31 O(n)*31 O(n)31,这样就可以得到正解了。
c c o d e : ccode: ccode:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+5;
const int M=3e6;
int son[M][2],idx,n,a[N],u,v,w,res;
struct edge{
    int u,v,w,next;
}edges[N<<2];
int head[N],tot;
void add_edge(int u,int v,int w)
{
    edges[tot].u=u;edges[tot].v=v;edges[tot].w=w;edges[tot].next=head[u];head[u]=tot++;
    edges[tot].u=v;edges[tot].v=u;edges[tot].w=w;edges[tot].next=head[v];head[v]=tot++;
}
void dfs(int u,int f,int sum)
{
    a[u]=sum;
    for(int i=head[u];~i;i=edges[i].next){
        if(edges[i].v!=f)dfs(edges[i].v,u,sum^edges[i].w);
    }
}
void insert(int x)
{
    int p=0;
    for(int i=30;~i;i--){
        int &s=son[p][x>>i&1];
        if(!s)s=++idx;
        p=s;
    }
}
int query(int x)
{
    int p=0,res=0;
    for(int i=30;~i;i--){
        int s=x>>i&1;
        if(son[p][!s]){
            res+=1<<i;
            p=son[p][!s];
        }else p=son[p][s];
    }
    return res;
}
int main()
{
    while(scanf("%d",&n)==1){
        memset(head,-1,sizeof(head));
        for(int i=0;i<n-1;i++){
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w);
        }
        dfs(0,-1,0);
        idx=0;
        memset(son,0,sizeof(son));
        for(int i=0;i<n;i++)insert(a[i]);
        res=0;
        for(int i=0;i<n;i++){
            res=max(res,query(a[i]));
        }
        printf("%d\n",res);
    }
    return 0;
}

<think>我们已知:1.定义dx[i]为根节点(假设为0)到节点i的路径上所有边权的异或值。通过一次DFS可以求出所有dx[i]。2.树上任意两点u和v之间的路径异或值等于dx[u]xordx[v](因为从根到u和根到v的路径中,重叠的部分会相互抵消,剩下u到v的路径)。3.问题转化为:在dx数组(有n个数)中,找到两个数,使它们的异或值最大。使用01-Trie树来解决最大异或对问题:-将每个数的二进制表示(从高位到低位)插入到Trie树中(固定位数,比如31位,因为w<2^31,所以最多31位)。-然后对于每个数,在Trie树中查询与它异或结果最大的数:从根开始,每次尽量走与当前位相反的位(这样异或后当前位为1),如果没有相反的位则走相同的位。步骤:1.建树:使用邻接表存储树结构。2.DFS遍历树,计算每个节点的dx值(从根节点0开始,根节点的dx值为0)。3.构建01-Trie:将所有dx值插入Trie树。4.对每个dx值,在Trie树中查询最大异或值,然后取所有结果的最大值。注意:由于树是无根的,我们任意选择一个根(比如0号节点)即可。代码实现细节:-Trie树节点:每个节点有两个孩子,分别表示0和1。-插入:从高位到低位,根据每一位的值(0或1)建立路径。-查询:从高位到低位,尽量选择与当前位不同的分支(如果存在),这样异或后该位为1。时间复杂度:O(n*31)≈O(n),可以接受。注意:多组测试数据,需要初始化。下面给出C++代码实现:</think>### 带权树上最长异或路径的C++实现(使用Trie优化) #### 算法核心思想 1. **路径异或转换** 定义$d[u]$为节点$u$到根节点的路径异或和,则任意两点$u,v$间路径的异或和为: $$ \text{XOR}(u,v) = d[u] \oplus d[v] $$ 这个性质成立是因为路径重叠部分会相互抵消[^2]。 2. **问题转化** 原问题等价于: $$ \max_{u,v \in V} \left( d[u] \oplus d[v] \right) $$ 3. **01-Trie优化** 使用二进制字典树高效查询最大异或对: - 将每个$d[i]$的二进制表示插入Trie - 对每个$d[i]$,在Trie中查询能产生最大异或值的路径 #### C++代码实现 ```cpp #include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 100010; const int BIT_LEN = 31; // 31位整数处理 struct Edge { int v, w; Edge(int _v, int _w) : v(_v), w(_w) {} }; vector<Edge> graph[MAXN]; int d[MAXN]; // 存储节点到根节点的异或和 // 01字典树节点 struct TrieNode { TrieNode* child[2]; TrieNode() { child[0] = child[1] = nullptr; } }; // DFS计算前缀异或和 void dfs(int u, int parent, int cur_xor) { d[u] = cur_xor; for (const Edge& e : graph[u]) { if (e.v == parent) continue; dfs(e.v, u, cur_xor ^ e.w); } } // 插入数字到字典树 void insertTrie(TrieNode* root, int num) { TrieNode* node = root; for (int i = BIT_LEN; i >= 0; i--) { int bit = (num >> i) & 1; if (!node->child[bit]) { node->child[bit] = new TrieNode(); } node = node->child[bit]; } } // 查询与num异或的最大值 int queryTrie(TrieNode* root, int num) { TrieNode* node = root; int max_xor = 0; for (int i = BIT_LEN; i >= 0; i--) { int bit = (num >> i) & 1; int toggled = 1 - bit; // 期望的相反位 if (node->child[toggled]) { max_xor |= (1 << i); node = node->child[toggled]; } else { node = node->child[bit]; } } return max_xor; } int main() { int n; while (cin >> n) { // 初始化 for (int i = 0; i < n; i++) graph[i].clear(); memset(d, 0, sizeof(d)); // 读入树结构 for (int i = 0; i < n-1; i++) { int u, v, w; cin >> u >> v >> w; graph[u].push_back(Edge(v, w)); graph[v].push_back(Edge(u, w)); } // DFS计算前缀异或和 dfs(0, -1, 0); // 构建字典树 TrieNode* root = new TrieNode(); for (int i = 0; i < n; i++) { insertTrie(root, d[i]); } // 查询最大异或值 int max_xor = 0; for (int i = 0; i < n; i++) { max_xor = max(max_xor, queryTrie(root, d[i])); } cout << max_xor << endl; // 释放字典树内存(实际竞赛可省略) // ... 遍历删除Trie节点 ... } return 0; } ``` #### 关键组件解析 1. **数据结构** - `Edge`:存储邻接点和边权 - `TrieNode`:字典树节点,包含0/1两个分支 - `d[]`:存储节点到根节点的异或和 2. **核心函数** - `dfs()`:深度优先遍历计算路径异或- `insertTrie()`:将数字插入01字典树 - `queryTrie()`:在字典树中查询最大异或值 3. **算法流程** ```mermaid graph TD A[输入树结构] --> B[DFS计算d数组] B --> C[构建01字典树] C --> D[遍历d数组查询最大异或] D --> E[输出结果] ``` #### 复杂度分析 - **时间复杂度**:$O(n \log W)$ DFS遍历$O(n)$,字典树操作$O(\log W)$,其中$W$为最大边权 - **空间复杂度**:$O(n \log W)$ 字典树存储所有二进制表示 #### 测试用例 ```plaintext 输入: 4 0 1 3 1 2 5 1 3 6 计算过程: d[0]=0(0000), d[1]=3(0011) d[2]=3^5=6(0110), d[3]=3^6=5(0101) 最大异或: d[0]^d[2]=0^6=6(0110) → 最大值 输出:6 ``` > 该实现基于路径异或性质[^2]和Trie树优化[^1],可高效处理$n \leq 10^5$的数据规模。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值