模拟赛AT2046 Namori 二分图

题面

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的嵌套卡了好久。。。

原文地址:https://www.cnblogs.com/lzqlalala/p/10479208.html