1. 程式人生 > >POJ 3764 The xor-longest Path ( 字典樹求異或最值 && 異或自反性質 && 好題好思想)

POJ 3764 The xor-longest Path ( 字典樹求異或最值 && 異或自反性質 && 好題好思想)

strong span -s node poj printf return blog pre

題意 : 給出一顆無向邊構成是樹,每一條邊都有一個邊權,叫你選出一條路,使得此路所有的邊的異或值最大。

分析 : 暴力是不可能暴力的,這輩子不可能暴力,那麽來冷靜分析一下如何去做。假設現在答案的異或值的最大的路的起點和終點分別為 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, 所有點) 都會到字典樹裏面去,出來的結果也自然是其中的兩個節點一個作為起點一個作為終點出來的路徑異或值。表達可能不太好,請見諒,看看代碼輔助理解吧……

技術分享
#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; }
View Code

瞎 : 想法非常好的題目,利用異或性質轉化思想需要學習

POJ 3764 The xor-longest Path ( 字典樹求異或最值 && 異或自反性質 && 好題好思想)