题面
Description
现在给你一张NN个点MM条边的连通图,我们保证N−1≤M≤NN−1≤M≤N,且无重边和自环。
每一个点都有一种颜色,非黑即白。初始时,所有点都是白色的。
想通过执行若干次某种操作的方式,来将所有的点变成黑色。操作方式如下:
选择一对颜色相同的相邻的节点(存在边直接连接彼此),将它们的颜色反转。即若原来都是白色,则都变成黑色,反之亦然。
现在想知道,他能否通过执行这种操作以达到目的。如果可以,他还希望步数尽可能的少。
Input
第一行有两个正整数NN和MM(2≤N≤1052≤N≤105,N−1≤M≤NN−1≤M≤N)
接下来MM行,每行2个正整数aa和bb(1≤a,b≤N1≤a,b≤N),表示每条边连接的两个点。
Output
如果存在操作方案能达到目的,请输出最少操作次数。
否则,请输出−1
解法
先考虑树
大佬们说这是一个套路:看到对相同颜色的点进行操作,就把他们建成不同颜色。
于是我们就先套路,对树进行分层黑白染色,于是原问题转化为进行多少次操作,可以让整棵树上颜色对调。
如果黑白颜色个数不同显然不行。
考虑一个子树的根,他和他父亲最少要操作子树内黑白颜色的差次,对于每一个节点维护这样一个S_i,
答案的下界就是S_i的绝对值的和,显然这个下界是可以取到的。
奇环
断开一条换上的边,两点是同色的,也就是说可以对这条边进行操作凭空让黑点或白点个数增加(将原来的白点看成白点,黑点看成白点理解即可)
如果此时黑白点个数不平衡,且差为偶数,就可以同过这条边平衡。
先断开这条边,计入答案和S_i,再维护S_i即可。
偶环
假设这条边转了x次,答案为$sum$ abs(Si-k*x),k只为1、-1或0,用中位数定理可求得最优的x
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+5;
int head[N],nxt[N<<1],to[N<<1],siz[N],z[N],k[N];
int n,m,num,X,Y,odd,sum,tot,mid;
LL ans;
int read()
{
int x=0,p=1; char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') p=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*p;
}
void add(int x,int y)
{
nxt[++num]=head[x],to[num]=y,head[x]=num;
}
void dfs1(int x,int fa)
{
for(int i=head[x]; i ;i=nxt[i])
{
int y=to[i];
if(y==fa) continue ;
if(siz[y])
{
if(siz[y]==siz[x]) odd=1;
X=x,Y=y;
}
else
{
siz[y]=-siz[x];
dfs1(y,x);
}
}
}
void dfs2(int x,int fa)
{
for(int i=head[x]; i ;i=nxt[i])
{
int y=to[i];
if(y==fa || (x==X&&y==Y) || (x==Y&&y==X))
continue ;
dfs2(y,x);
siz[x]+=siz[y];
k[x]+=k[y];
}
}
int main()
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
siz[1]=1;
dfs1(1,0);
for(int i=1;i<=n;i++) sum+=siz[i];
if(n-1==m)
{ if(sum) return printf("-1
"),0; }
else
if(odd)
{
if(sum&1) return printf("-1
"),0;
ans+=abs(sum>>1);
siz[X]-=sum>>1,siz[Y]-=sum>>1;
}
else
{
if(sum) return printf("-1
"),0;
k[X]=1,k[Y]=-1;
}
dfs2(1,0);
for(int i=1;i<=n;i++)
if(k[i]) z[++tot]=k[i]*siz[i];
else ans+=abs(siz[i]);
z[++tot]=0;
sort(z+1,z+tot+1);
mid=z[tot+1>>1];
for(int i=1;i<=tot;i++) ans+=abs(z[i]-mid);
printf("%lld
",ans);
return 0;
}
被if、else的嵌套卡了好久。。。