1. 程式人生 > >讓我們異或吧

讓我們異或吧

題目 路徑 不錯 思路 一個數 不用 答案 。。 維護

洛谷原創的這道題說實話真的不錯。。。

題目大意:

求一個樹上兩個點路徑所有邊的異或和。

樹上求和問題很顯然會想到LCA吧,我們假定兩個點是u和v,他們的LCA為lca(a,b)

我們維護一個dis數組,dis[i]代表從根節點到i的路徑邊權的異或和。

我們要求的是兩點之間的異或和,我們想從做過的題型中找思路:如果只是簡單的求和的話,我們只要用a,b的dis減去兩倍lca的dis即可。

但是現在是異或,我們還是從這上去想:我們利用lca來作為中間量。

我們會發現一個數連續異或兩次就等於沒有異或,並且x^0=x(顯然的吧。。。)

所以我們求兩點路徑間的異或和就可以轉化成求兩點到根節點的dis的異或和。

所以本題不用lca,只需要遍歷一遍樹,求出每個點的dis就可以了。

答案就是dis[a]^dis[b].

最後,附上本題代碼:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define maxn 100000
 4 using namespace std;
 5 
 6 int n,head[maxn+5],cnt,m;
 7 int dep[maxn+5],dis[maxn+5];
 8 struct EDGE
 9 {
10     int to,nxt,v;
11 };
12 EDGE edge[maxn*2
+5]; 13 14 void add(int x,int y,int z) 15 { 16 edge[++cnt].to=y; 17 edge[cnt].v=z; 18 edge[cnt].nxt=head[x]; 19 head[x]=cnt; 20 } 21 void pre_fir(int fa,int u) 22 { 23 for(int i=head[u]; i; i=edge[i].nxt) 24 { 25 if(edge[i].to==fa) continue; 26 dis[edge[i].to]=dis[u]^edge[i].v;
27 pre_fir(u,edge[i].to); 28 } 29 } 30 int main() 31 { 32 scanf("%d",&n); 33 for(int i=1; i<=n-1; i++) 34 { 35 int x,y,z; 36 scanf("%d%d%d",&x,&y,&z); 37 add(x,y,z); 38 add(y,x,z); 39 } 40 scanf("%d",&m); 41 pre_fir(0,1); 42 int a,b; 43 for(int i=1; i<=m; i++) 44 { 45 scanf("%d%d",&a,&b); 46 printf("%d\n",dis[a]^dis[b]); 47 } 48 return 0; 49 }

讓我們異或吧