传送门
题目:
Example
input
5
2 2
1 3
1 2
5 5
3 3 3 3 3
1 2
2 3
1 4
4 5
5 2
1 7 2 3 5
1 2
2 3
1 4
4 5
5 3
1 6 4 1 2
1 2
2 3
1 4
4 5
3 3
1 7 4
1 2
2 3
output
NO
YES
NO
YES
NO
大致题意:
给出包含
n
n
n个节点
n
−
1
n-1
n−1条边的一棵树,每个节点有对应的点权,
给出
k
k
k,要求删除至少一条边,至多
k
−
1
k-1
k−1条边,
若存在某种方案,使删边后的森林中,每棵树的所有节点总异或值都相等,输出
y
e
s
yes
yes,反之
n
o
no
no。
思路:
由已知得到异或和的规律为:
x
⨁
x
=
0
x \bigoplus x = 0
x⨁x=0
x
⨁
x
⨁
x
=
x
x \bigoplus x\bigoplus x = x
x⨁x⨁x=x
所以:
假设原树的总异或和为
x
x
x,分析
k
k
k可知,最终方案有两种可能:删一条边(拆为偶数个)或删两条边(拆为奇数个):
1、若
x
=
0
x=0
x=0,随意断开一条边即可。(此时一定是异或和相等的两棵树)
x
⨁
x
=
0
x \bigoplus x = 0
x⨁x=0
2、若
x
!
=
0
x!=0
x!=0,若能成立则存在断开后一定为每棵树异或和均等于原树的总异或和
x
x
x,且需要找到至少两个。
s
o
so
so:若我们能找到到两块不相交的异或和为原树总异或和
x
x
x的连通块,且
k
−
1
k-1
k−1的值需要大于或等于
2
2
2(断开两条边,形成三棵树)
x
⨁
x
⨁
x
=
x
x \bigoplus x\bigoplus x = x
x⨁x⨁x=x
(我们可以通过找到根为v的树异或和等于原树的总异或和x后将此位置的值标记为0,即可避免连通块出现相交的情况)
通过dfs将以每个节点为根的子树都找一遍
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int a[N],b[N],num,sum;
vector<int>g[N];
void dfs(int u,int fa){
b[u]=a[u];
for(auto v:g[u]){
if(v^fa){//不包括fa,遍历此时根u的子树所有点
dfs(v,u);
b[u]^=b[v];
}
}
if(b[u]==sum){
num++;//计数成功的个数
b[u]=0;//将确定可以成功的子树删去
}
}
int main(){
int t,n,k;cin>>t;
while(t--){
sum=0,num=0;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
sum^=a[i];
g[i].clear();
}
for(int i=0;i<n-1;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
if(sum==0)cout<<"YES"<<endl;
else{
dfs(1,0);
if(k-1>=2&&num>=2)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
return 0;
}