题意
给定一棵有 n n n个节点的树,求删去 1 1 1~ k k k − - − 1 1 1条边后,是否能让形成的各个连通块的异或和相同。
做法
- 令所有点的异或和为 m m m,观察发现当 m = 0 m=0 m=0时,删去任意一条边都是可行方案;当 m ! = 0 m!=0 m!=0时,删去边后得到的各个连通块的异或和都是 m m m。
- 考虑上述第二种情况,我们发现此时连通块的个数一定是奇数,所以至少要删去两条边,所以当 k = 2 k=2 k=2时显然是无法符合题意的。
- 我们还可以发现连通块的个数一定是3个,因为大于3个时可以通过合并任意偶数个连通块来使个数变成3个。
- 令 1 1 1为根节点,我们可以从下往上找异或和为 m m m的子树,找到一个后就删去该子树,然后继续找下一个。若能找到的这种子树的个数大于等于 2 2 2,则符合题意。
代码
#include<cstdio>
#include<cstring>
#define ri register int
using namespace std;
const int maxn=1e5+21;
struct E{
int v,n;
}e[maxn<<1];
int head[maxn],cnt;
int a[maxn],sum;
int flag[maxn];
int n,k,tot;
inline int read(){
int x=0;char c=getchar();
while(c>'9'||c<'0') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x;
}
void add(int u,int v){
e[++cnt]={v,head[u]};
head[u]=cnt;
}
void clear(){
sum=cnt=tot=0;
for(ri i=1;i<=n;++i) head[i]=0;
for(ri i=1;i<=cnt;++i) e[i]={0,0};
}
int dfs(int u,int f){
for(ri i=head[u];i;i=e[i].n){
int v=e[i].v;
if(v==f) continue;
int tmp=dfs(v,u);
if(tmp==sum) tot++;
else a[u]^=tmp;
}
return a[u];
}
void solve(){
n=read(),k=read();
clear();
for(ri i=1;i<=n;++i) a[i]=read(),sum^=a[i];
for(ri i=1;i<n;++i){
int u=read(),v=read();
add(u,v),add(v,u);
}
if(!sum){//特判1
printf("YES\n");
return;
}
if(k==2){//特判2
printf("NO\n");
return;
}
dfs(1,0);
printf(tot>1?"YES\n":"NO\n");
}
int main(){
int T=read();
while(T--) solve();
return 0;
}