解题思路:由树的归一性和异或的充分性,容易证明在每个点异或和不等于0的情况下,分成的连通块只能是奇数个。由于树的单链性,最终的每个连通块的异或和只能是所有点值的异或和。
考虑到树的性质,再结合异或的性质,发现偶数个连通块都可以化归为同一个连通块,最终可以意识到,在同一棵树上,如果能够分成奇数个连通块,那么最好最简单且正确的转化方式
一定是转化为三个连通块。或许其中的一个连通块可以分成三个值相等的小连通块,这也是可以接受的。我们只需要最终能利用异或的性质把它们分成起码三个。但是由反证法知道,如果一个连通块不能被分为奇数个值正确的小连通块,那么大连通块的值也必然不正确。换句话讲,我们最后需要的只是能凑出所有点异或和的连通块。
而树上的连通块一定可从子树考虑分割,即DFS。
出题思路:这个题的数据不容易做出来。事实上,在绝大部分情况下,输出的都是"NO"。话句话讲,题意本身就完全是人为的特殊情况。
#include<cstdio> #include<iostream> using namespace std; const int MAXN=2e5; struct Edge{ int u,v,nxt; }e[MAXN*2]; int head[MAXN],a[MAXN],cnt=0; void addEdge(int u,int v){ e[++cnt].u=u; e[cnt].v=v; e[cnt].nxt=head[u]; head[u]=cnt; } int needSum=0;//需要的^和 int okCnt=0;//可以分割的子树 int dfs(int nowX,int fa){ int tmpSum=a[nowX]; for(int i=head[nowX];i;i=e[i].nxt){ int nowV=e[i].v; if(nowV!=fa){ tmpSum^=dfs(nowV,nowX); } } if(tmpSum==needSum){ okCnt++; return 0; }else{ return tmpSum; } } void init(){ cnt=0; needSum=0; okCnt=0; } int main(){ int t; scanf("%d",&t); while(t--){ init(); int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); needSum^=a[i]; head[i]=0; } for(int i=1;i<=n-1;i++){ int u,v; scanf("%d%d",&u,&v); addEdge(u,v); addEdge(v,u); } if(needSum==0){ printf("YES "); }else{ dfs(1,0); if(okCnt>=3&&k>=3){ printf("YES "); }else{ printf("NO "); } } } return 0; }