讓我們異或吧
阿新 • • 發佈:2019-03-23
題目 路徑 不錯 思路 一個數 不用 答案 。。 維護
洛谷原創的這道題說實話真的不錯。。。
題目大意:
求一個樹上兩個點路徑所有邊的異或和。
樹上求和問題很顯然會想到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 }
讓我們異或吧