题意
有一颗树,问你能不能分成至少2个至多k个连通分量,并且每个连通分量的异或值都相同。
解析
设每个点的异或之和为xo
- 根据异或的性质,如果xo为0,那么说明可以分成两个区间,其两个区间的异或之和一定为0。
- 如果xo不为0,我们要分成m个区间,每个区间为xo,如果我们能分成10个区间,那么一定能分成8个区间,因为我们可以选择相邻的三个连通分量构成一个大连通分量,其异或值还是为xo,因此,我们最后分成的奇数个连通分量的最小值是3。
- 现在题目转化成,能不能分成3个连通分量,异或值相同。
- 思路很简单,dfs遍历每个点的所有子树,如果其自身和子树的异或之和为xo,那么就断链并且记录个数cnt++,断链的方式就是给父节点返回0。
- 如果最后cnt>=3 && k>=3 那么表示满足题意。
代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e5+5;
vector<int>g[N];
int r[N];
int xo=0,cnt=0;
void init(int n){
xo=0;
cnt=0;
for(int i=0;i<=n;i++){
g[i].clear();
}
}
int dfs(int x,int pre){
int son=r[x];
for(int j:g[x]){
if(pre!=j){
son^=dfs(j,x);
}
}
if(son==xo){
son=0;
cnt++;
}
return son;
}
void solve(){
int n,m;
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=n;i++){
scanf("%d",&r[i]);
xo=xo^r[i];
}
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1,1);
if((cnt>=3 && m>=3) || xo==0){
puts("YES");
}
else{
puts("NO");
}
}
int main() {
int t ;
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}