POJ 3764 The xor-longest Path ( 字典树求异或最值 && 异或自反性质 && 好题好思想)
题意 : 给出一颗无向边构成的树,每一条边都有一个边权,叫你选出一条路,使得此路所有的边的异或值最大。
分析 : 暴力是不可能暴力的,这辈子不可能暴力,那么来冷静分析一下如何去做。假设现在答案的异或值的最大的路的起点和终点分别为 a、b,这个异或值为了方便我用 ⊕(a, b) 表示,那么接下来有一个巧妙的转化 ⊕(a, b) == ⊕(root, a) ^ ⊕(root, b) ,这个转化在 LCA(a, b) == root 的时候明显成立,因为重复的部分会异或两次,相当于没有异或,而LCA(a, b) != root的时候,同样也可以使用上述转化来进行运算,因为这是一棵树,所以最后无论哪两个节点,都至少会有 root 是这两个节点的祖先,因此不必担心 a 和 b 在上述运算出来的结果会有 a 和 b 不联通的情况,那么这题就很好做了,只要从根节点开始DFS,每一次将新节点异或得到新的异或值并丢到字典树里面,这样所有的 ⊕(root, 所有点) 都会到字典树里面去,出来的结果也自然是其中的两个节点一个作为起点一个作为终点出来的路径异或值。表达可能不太好,请见谅,看看代码辅助理解吧……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<stdio.h> #include<string.h> #include<malloc.h> #include<algorithm> #define LL long long using namespace std; const int Bit = 30; const int maxn = 100010; int tot = 0; int ans; struct Edge{ int v, Next, weight; }; Edge e[maxn<<1]; int Head[maxn]; bool vis[maxn]; inline void Add_Edge(int v, int to, int weight) { e[tot].v = to; e[tot].Next = Head[v]; e[tot].weight = weight; Head[v] = tot++; } struct Trie { Trie *ch[2]; int v; inline void init(){ for(int i=0; i<2; i++) this->ch[i] = NULL; this->v = -1; }; } Heap[maxn<<10]; int node_cnt = 0; Trie *root; Trie * New() { Heap[node_cnt].init(); return &Heap[node_cnt++]; } inline void CreateTrie(int x) { Trie * p = root, *tmp; for(int i=Bit; i>=0; i--){ int idx = (x>>i)&1; if(p->ch[idx] == NULL){ p->ch[idx] = New(); }p = p->ch[idx]; } p->v = x; } int Query(int x) { Trie *p = root; for(int i=Bit; i>=0; i--){ int idx = (x>>i)&1; if(p->ch[idx^1]){ p = p->ch[idx^1]; }else p = p->ch[idx]; } return (p->v) ^ x; } void DFS(int u, int Val) { CreateTrie(Val);///将路径的权值全丢到字典树里面去,代表⊕(root, u) vis[u] = true; for(int i=Head[u]; i!=-1; i=e[i].Next){ int v = e[i].v; int w = e[i].weight; if(!vis[v]){ ans = max(ans, Query(Val ^ w));///查询更新最值 DFS(v, Val^w); } } } inline void INIT() { node_cnt = 0; root = New(); memset(Head, -1, sizeof(Head)); memset(vis, false, sizeof(vis)); ans = tot = 0; } int main(void) { int n; while(~scanf("%d", &n)){ INIT(); for(int i=1; i<=n-1; i++){ int u, v, w; scanf("%d %d %d" ,&u, &v, &w); u++, v++; Add_Edge(u, v, w); Add_Edge(v, u, w); } DFS(1, 0); printf("%d\n", ans); } return 0; }
瞎 : 想法非常好的题目,利用异或性质转化思想需要学习