树形DP,主要思想为:自根节点开始遍历,一直遍历到叶子节点,后从叶子节点开始将得到的信息不断向上传递,知道根节点为止。
题目描述:
给定一棵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
说明
再将这6个数异或起来就可以得到答案5了。
链接:https://ac.nowcoder.com/acm/contest/272/B
来源:牛客网
每个节点含有对应的权值,求解所有路径中,经过节点权值的异或和。
一个数异或它本身为0,即偶数次异或本身为0,奇数次异或本身等于数字本身,所以,我们只要求解所有路径中,经过每一个节点的次数即可
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
typedef long long ll;
const int maxn = 500100;
const int maxm = 1000100;
int head[maxm],next1[maxm],v[maxm],val[maxn];
ll times[maxn];
int n,k;
void Add_edge(int x,int y)
{
v[k] = y;next1[k] = head[x];head[x] = k++;
v[k] = x;next1[k] = head[y];head[y] = k++;
}
int dfs(int p,int f)
{
int sum = 0;
for(int i = head[p];i != -1;i = next1[i])
{
int vv = v[i];
if(vv != f)
{
ll temp = dfs(vv,p);
times[p] += (ll)temp * sum; //子树之间的路径经过次数
sum += temp; //当前节点子节点的节点数量
}
}
times[p] += (ll)(n - sum -1) * sum; //作为中间点,子树和其他节点路径中经过的次数
times[p] += n-1; //本身作为重点经过的次数
return sum + 1;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
int x,y;
for(int i = 0;i < n-1;i ++)
{
scanf("%d%d",&x,&y);
Add_edge(x,y);
}
for(int i = 1;i <= n;i ++)
scanf("%d",val+i);
dfs(1,0);
int ans = 0;
for(int i = 1;i <= n;i ++)
if(times[i] % 2)
ans ^= val[i];
printf("%d\n",ans);
}