题意
给出一颗树,以及树上的每条边的关系和点的权值,求在任意两个点的最短路径的走法下,点权异或和是多少。解析
首先不能暴力,因为数据范围到了 $5e5$ 暴力妥妥的超时那么我们考虑其他的做法,首先是异或和, ;
所以我们将题目转换为在最短路径下通过节点的奇偶次数,
首先我们先只考虑经过u而不以u为最短路终点的情况看,我们可以一次dfs统计出各个子树的大小
并且知道子树大小后假设sz[u]代表u节点包括自己在内的子树节点总数之和并且知道子树大小后假设sz[u]代表u节点包括自己在内的子树节点总数之和
那么把v全部遍历一遍dfs可以做到,在dfs过程中那么把v全部遍历一遍dfs可以做到,在dfs过程中
又因为是树形结构上方的点或者兄弟节点的子树到v的子树中是一定要经过u的这里可以算到经过次数
但是再考虑重复性,假设u有2棵子树,左子树到右子树算了一遍,右子树到左子数又算了一遍
很明显算了两遍,我们上面的求和只把相邻子树计算两次,
但是子树和u的上方节点只是计算了一次,所以我们应该补上这一些次数再/2即上方求和基础上加上,就把子树和u上方节点计算了两遍,最终/2就是第一类的次数了
第二:以u为终点,其余n-1个点为起点的最短路一定是需要u的,这个很简单思考,直接就是n-1次经过u第二:
所以答案就是把上方总结的两种情况求和再判断奇偶看是否进行这个点的异或处理即可!
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=5e5+7; int head[MAXN],tot; struct Node{ int v,next; }edge[MAXN<<1]; int n,w[MAXN],sz[MAXN]; //n表示树的节点个数,w储存节点权值,sz表示每一个节点的子树打大小 int ans; void add_edge(int u,int v){ tot++; edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot; } void dfs(int u,int fa){ sz[u]=1; int num=n-1; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].v; if(v==fa) continue; dfs(v,u); num+=sz[v]*(n-sz[v]); sz[u]+=sz[v]; } num+=sz[u]*(n-sz[u]); num/=2; if(num&1) ans^=w[u]; } int main(void){ scanf("%d",&n); memset(head,0,sizeof(head)); tot=0; for(int i=1;i<=n-1;++i){ int u,v; scanf("%d%d",&u,&v); add_edge(u,v); add_edge(v,u); } for(int i=1;i<=n;++i) scanf("%d",&w[i]); ans=0; dfs(1,0); printf("%d\n",ans); return 0; }