题意
给定一棵有nnn个节点的树,求删去111~kkk−-−111条边后,是否能让形成的各个连通块的异或和相同。
做法
- 令所有点的异或和为mmm,观察发现当m=0m=0m=0时,删去任意一条边都是可行方案;当m!=0m!=0m!=0时,删去边后得到的各个连通块的异或和都是mmm。
- 考虑上述第二种情况,我们发现此时连通块的个数一定是奇数,所以至少要删去两条边,所以当k=2k=2k=2时显然是无法符合题意的。
- 我们还可以发现连通块的个数一定是3个,因为大于3个时可以通过合并任意偶数个连通块来使个数变成3个。
- 令111为根节点,我们可以从下往上找异或和为mmm的子树,找到一个后就删去该子树,然后继续找下一个。若能找到的这种子树的个数大于等于222,则符合题意。
代码
#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;
}

本文介绍了一种算法,针对给定的有n个节点的树,如何通过删除111到kkk-1的边,使得剩余连通块的异或和相等。方法包括特判、连通块分析和深度优先搜索,适用于特定条件下的异或和操作。
1326

被折叠的 条评论
为什么被折叠?



