codeforces1592:C. Bakry and Partitioning
题目大意
题目给出了一颗有
n
n
n 个顶点的树,每个点都有一个权值
a
i
a_i
ai ,求可否满足去掉最少一条边,最多
k
k
k 条边后,剩下的连通块的异或和相等。
题解
这道题,我们可以分两种情况讨论:首先,如果所有点的异或和
x
o
r
s
u
m
xorsum
xorsum 是
0
0
0 那么,一定可以任意将这颗树分成两个连通块,此时这两个连通块的异或值是相等的。
其次,如果
x
o
r
s
u
m
xorsum
xorsum 不为
0
0
0 那么我们肯定会将整棵树分成奇数块连通块。假设分成
m
m
m 块连通块 (
m
m
m 为奇数 ):
B
1
,
B
2
,
B
3
,
.
.
.
.
,
B
m
B_1 ,B_2 ,B_3,....,B_m
B1,B2,B3,....,Bm 由于每一块的异或值都是一样的,那么就存在:
B
1
x
o
r
B
2
x
o
r
B
3
x
o
r
B
m
−
2
=
B
m
−
1
=
B
m
=
B
1
B_1\quad xor \quad B_2\quad xor\quad B_3 \quad xor \quad B_{m-2}= B_{m-1}=B_m=B_1
B1xorB2xorB3xorBm−2=Bm−1=Bm=B1
至此,我们只要找到三个异或和相等的连通块就可以成立,并且每个连通块的异或和就是总的异或和。
我们可以通过dfs去寻找子树(包含这个点)异或和等于
x
o
r
s
u
m
xorsum
xorsum 的点,将它从树上去掉,然后在剩下异或和为
0
0
0 的树中找到子树异或和为
x
o
r
s
u
m
xorsum
xorsum 的点。 都找到说明满足条件。
#include<bits/stdc++.h>
using namespace std;
int n,k;
const int N=1e5+10;
int xsum[N],in[N],out[N],a[N],f[N];
std::vector<int>vt[N];
int dfn;
int dfs(int u,int fa)
{
in[u]=++dfn;
f[u]=fa;int sum=a[u];
for(auto v:vt[u])
{
if(v==fa)continue;
sum^=dfs(v,u);
}
xsum[u]=sum;
out[u]=++dfn;
return sum;
}
void dfst(int u,int &p1,int &ck)
{
if(xsum[u]==0||xsum[u]==ck)
{
p1=u;
}
for(auto v:vt[u])
{
if(v==f[u])continue;
dfst(v,p1,ck);
}
}
int main()
{
int t;cin>>t;
while(t--)
{
int sum=0;dfn=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)vt[i].clear();
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
sum^=a[i];
}
for(int i=1;i<n;++i)
{
int u,v;
scanf("%d%d",&u,&v);
vt[u].push_back(v);
vt[v].push_back(u);
}
if(!sum)
{
printf("YES\n");
continue;
}
dfs(1,0);
int p1=-1;
dfst(1,p1,sum);
if(xsum[p1]==sum)
{
int tmp=f[p1];
while(tmp!=0)
{
xsum[tmp]^=sum;
tmp=f[tmp];
}
}
if(p1==-1)
{
printf("NO\n");
continue;
}
int ans=0;
for(int i=1;i<=n;i++)
{
if(xsum[p1]==sum&&(in[i]<in[p1]||in[i]>out[p1])&&xsum[i]==sum)
{
if(k>2)ans=1;
}
if(xsum[p1]==0&&(in[i]>in[p1]&&in[i]<out[p1])&&xsum[i]==sum)
{
if(k>2)ans=1;
}
}
if(ans)
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
}