「LOJ#10056」「一本通 2.3 練習 5」The XOR-longest Path (Trie 「LOJ#10050」「一本通 2.3 例 2」The XOR Largest Pair (Trie
阿新 • • 發佈:2018-11-08
#10056. 「一本通 2.3 練習 5」The XOR-longest Path
題目描述
原題來自:POJ 3764
給定一棵 nnn 個點的帶權樹,求樹上最長的異或和路徑。
輸入格式
第一行一個整數 nnn,接下來 n−1n-1n−1 行每行三個整數 u,v,wu,v,wu,v,w,表示 u,vu,vu,v 之間有一條長度為 www 的邊。
輸出格式
輸出一行一個整數,表示答案。
樣例
樣例輸入
4
1 2 3
2 3 4
2 4 6
樣例輸出
7
樣例解釋
最長的異或和路徑是 1→2→31\to 2\to 31→2→3 ,它的長度是 3⨁4=73 \bigoplus 4=73⨁4=7。
注意:結點下標從 111 開始到 NNN。
注:x⨁yx \bigoplus yx⨁y 表示 xxx 與 yyy 按位異或。
資料範圍與提示
對於 100%100\%100% 的資料,1≤n≤105,1≤u,v≤n,0≤w<2311\le n\le 10^5,1\le u, v \le n,0 \le w < 2^{31}1≤n≤105,1≤u,v≤n,0≤w<231
題解
首先對於樹上兩點路徑的異或值,可以用一個樹上字首和維護。
記$sum[x]$為$x$到祖先的異或和。
由於異或有:$a ⨁ a = 0$
所以如下圖,在$sum[u] ⨁ sum[v]$時,lca以上的屎色線已經被消掉了。
所以$ans=sum[u] ⨁ sum[v]$
問題轉化為:有1e5個數,要求其中兩數異或的最大值。
於是變為「LOJ#10050」「一本通 2.3 例 2」The XOR Largest Pair (Trie
於是這道題就可以由兩道看起來離得很遠的題拼起來而成了。
1 編號 題目 狀態 分數 總時間 記憶體 程式碼 / 答案檔案 提交者 提交時間 2 #231397 #10056. 「一本通 2.3 練習 5」The XOR-longest Path Accepted 100 225 ms 10364 KiB C++ / 1.8 K qwerta 2018-10-16 16:53:15 3 4 #include<iostream> 5 #include<cstring> 6 #include<cstdio> 7 #include<cmath> 8 using namespace std; 9 inline int read() 10 { 11 char ch=getchar(); 12 int x=0; 13 while(!isdigit(ch))ch=getchar(); 14 while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} 15 return x; 16 } 17 const int MAXN=1e5+3; 18 struct emm{ 19 int e,f,v; 20 }a[2*MAXN];//用來建樹 21 int h[MAXN]; 22 int tot=0; 23 void con(int x,int y,int l)//連樹邊 24 { 25 a[++tot].f=h[x]; 26 h[x]=tot; 27 a[tot].e=y; 28 a[tot].v=l; 29 a[++tot].f=h[y]; 30 h[y]=tot; 31 a[tot].e=x; 32 a[tot].v=l; 33 return; 34 } 35 int d[MAXN],w[MAXN];//記深度和字首和 36 void dfs(int x)//dfs遍歷樹 37 { 38 for(int i=h[x];i;i=a[i].f) 39 if(!d[a[i].e]) 40 { 41 w[a[i].e]=(w[x] xor a[i].v); 42 d[a[i].e]=d[x]+1; 43 dfs(a[i].e); 44 } 45 return; 46 } 47 struct ahh{ 48 int nxt[2]; 49 }tr[3200003];//Trie樹 50 int cnt=0; 51 int b[33];//用來按位拆分 52 void add(int x) 53 { 54 int j=-1; 55 memset(b,0,sizeof(b)); 56 while(x)//拆二進位制 57 { 58 b[++j]=x&1; 59 x>>=1; 60 } 61 int k=0; 62 for(int j=31;j>=0;--j) 63 { 64 if(!tr[k].nxt[b[j]]) 65 tr[k].nxt[b[j]]=++cnt; 66 k=tr[k].nxt[b[j]]; 67 } 68 return; 69 } 70 long long find(int x)//返回與x異或的最大結果 71 { 72 int j=-1; 73 memset(b,0,sizeof(b)); 74 while(x) 75 { 76 b[++j]=x&1; 77 x>>=1; 78 } 79 long long now=0; 80 int k=0; 81 for(int j=31;j>=0;--j) 82 { 83 if(tr[k].nxt[1-b[j]])//儘量往不一樣的走 84 { 85 now+=(1<<j); 86 k=tr[k].nxt[1-b[j]]; 87 } 88 else k=tr[k].nxt[b[j]]; 89 } 90 return now; 91 } 92 int main() 93 { 94 //freopen("a.in","r",stdin); 95 int n=read(); 96 for(int i=1;i<n;++i) 97 { 98 int u=read(),v=read(),w=read(); 99 con(u,v,w);//連樹邊 100 } 101 int s=min(7,n); 102 d[s]=1; 103 dfs(s); 104 long long ans=0; 105 for(int i=1;i<=n;++i) 106 add(w[i]);//加字首和 107 for(int i=1;i<=n;++i) 108 ans=max(ans,find(w[i]));//記錄答案 109 cout<<ans; 110 return 0; 111 }