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

牛客練習賽32B Xor Path

連結:https://ac.nowcoder.com/acm/contest/272/B
來源:牛客網
時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 262144K,其他語言524288K
64bit IO Format: %lld
題目描述
給定一棵n個點的樹,每個點有權值。定義表示 到 的最短路徑上,所有點的點權異或和。
對於,求所有的異或和。
輸入描述:
第一行一個整數n。
接下來n-1行,每行2個整數u,v,表示u,v之間有一條邊。
第n+1行有n個整數,表示每個點的權值。
輸出描述:
輸出一個整數,表示所有的異或和,其中。
示例1
輸入
複製
4
1 2
1 3
1 4
1 2 3 4
輸出
5

給一棵樹,求所有任意兩點路徑上異或和的和
首先由異或的性質得到,我們只要求出每個點作為路徑上的一點多少次(即被經過多少次)即可
然後可以考慮用樹形dp來解決,先dfs下去之後根據子樹的資訊來更新父節點
這道題的狀態轉移分了幾個部分,對一個節點來說,經過他的情況有幾種,
1 子樹每個節點都連向他,即num[u]+=(siz[u]-1)
2 他連向他的父節點的其他子樹節點(兄弟子樹),即num[u]+=(n-siz[u])
3 子樹經過他和其他子樹節點相連,即

for v in u.vec:
	tmp+=siz[v]*(siz[u]-1-siz[v]);
num[u]+=tmp/2

4 最後一個就是子樹節點經過他和他的兄弟子樹相連,即nun[u]+=(siz[u]-1)*(n-siz[u])

程式碼:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+50;
int siz[N];
ll val[N];
int n,u,v;
struct Edge{
    int v,next;
}edge[N*2];
int cnt,head[N];
void init(){
    cnt=0;
    memset(head,-1,sizeof
(head)); } void addEdge(int u,int v){ edge[cnt]=Edge{v,head[u]}; head[u]=cnt++; edge[cnt]=Edge{u,head[v]}; head[v]=cnt++; } ll num[N]; void dfs(int u,int f){ siz[u]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==f){ continue; } dfs(v,u); siz[u]+=siz[v]; } num[u]=(siz[u]-1)*(n-siz[u]); ll tmp=0; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==f){ continue; } tmp+=siz[v]*(siz[u]-1-siz[v]); } num[u]+=tmp/2; num[u]+=siz[u]-1; num[u]+=(n-siz[u]); } int main(void){ scanf("%d",&n); init(); for(int i=0;i<n-1;i++){ scanf("%d%d",&u,&v); addEdge(u,v); } for(int i=1;i<=n;i++){ scanf("%lld",&val[i]); } dfs(1,0); ll ans=0; for(int i=1;i<=n;i++){ //printf("%lld ",num[i]); if(num[i]%2){ ans^=val[i]; } } //printf("\n"); printf("%lld\n",ans); return 0; }