1. 程式人生 > >牛客練習賽32 B-Xor-Path 技巧題

牛客練習賽32 B-Xor-Path 技巧題

原題:題目連結

  題目大意:

  給定一棵樹,求樹上所有點對之間路徑上的值的異或和,即對所有點對( i , j )n >= i >= 1,n >= j >= 1且i != j,這些所有的點對間路徑上的值的異或和,的異或和

  解題大意:

  根據Xor的性質,同一個數抑或偶數次為0,奇數次為它本身,所以只要知道一個節點值被Xor的次數,就知道它對答案有沒有貢獻,最後把那些被Xor奇數次的值,即Xor後不為0的值,求它們的Xor和就是答案。

  這裡DFS一遍,就可以算出每個節點被Xor多少次。一個節點被Xor的次數分成兩部分,從它出發到其他(n-1)個節點的路徑數,和經過它的路徑數。算經過它的路徑時,就有點技巧:

  怎樣不重不漏地算出這些節點經過它的兩兩之間的路徑數。對於一個結點,搜它的子樹,記已經搜到的節點數為sum,每次搜一個枝,得到temp個點,temp*sum,就是這temp個節點與sum個節點間經過本節點的路徑數,然後把temp加到sum裡面去,繼續搜下一個枝,進行同樣的操作即可,然後再考慮子樹節點與非子樹節點間的路徑。仔細思考就能發現這就不重不漏了。之前想到複雜度很高的方法,TLE了

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7
; const int N = 1e6; int n,m,cnt; int head[N]; int val[N]; bool f[N]; struct Edge { int to,next; }e[N]; void add(int u,int v) { e[cnt].to=v; e[cnt].next=head[u]; head[u]=cnt++; } int dfs(int now,int fa) { int sum=0; long long t=0; int temp; for(int i=head[now];i!=-1
;i=e[i].next) { if(e[i].to!=fa) { temp=dfs(e[i].to,now); //每次搜尋上來 t+=(temp*sum)%2; //與之前搜到的和相乘,即可,只判斷奇偶,%2即可 sum+=temp; } } t=(t+n-1)%2; //從本節點出發的路徑 t+=((n-sum-1)*sum)%2; //所有從子樹節點出發,到非子樹節點的路徑 if(t%2) f[now]=1; return sum+1; } int main() { memset(f,0,sizeof(f)); memset(head,-1,sizeof(head)); scanf("%d",&n); int u,v; for(int i=1;i<n;++i) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(int i=1;i<=n;++i) scanf("%d",&val[i]); dfs(1,-1); int a=0; for(int i=1;i<=n;++i) { if(f[i]) a^=val[i]; } printf("%d\n",a); }