CF1592C Bakry and Partitioning

题意

给定一棵有 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
#include
#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;
}

你可能感兴趣的:(Codeforces,题解,算法)