CF600F Edge coloring of bipartite graph

一、题目

点此看题

二、解法

\(\tt vizing\) 定理板题,可以去看看 oiwiki

当然我不会证这个定理,我只会给出二分图背景下这个定理的构造性证明。

结论:二分图的边染色最小颜色数是点的最大度数

考虑增量法构造,现在考虑边 \((x,y)\) 的染色,设点 \(x\) 未使用的最小颜色是 \(lx\),设点 \(y\) 未使用的最小颜色是 \(ly\),如果 \(lx=ly\) 那么直接把这条边染色成 \(lx\);否则从 \(y\) 开始找到一条 \(lx,ly,lx...\) 的"增广路",把这一条增广路上所有边的"反转"即可。

这种做法的正确性来源于增广路径不自交,因为除 \(x\) 以外的点都把 \(lx,ly\) 的边拿去增广了,以后的增广不会有这种颜色的边连进来,又因为 \(lx\)\(x\) 未使用过的最小颜色,所以路径不会回到 \(x\),这也说明了增广路径是有限的。

因为每次取的是最小的未使用颜色,所以所有边的颜色都不超过最大度数。

三、应用

给你一张左部 \(n\) 个点、右部 \(m\) 个点的二分图,给每条边染 \([1,c]\) 中的某一种颜色,定义一个点的权值为出现次数的最大值减去出现次数的最小值,试构造方案使得最后总权值最小。

答案下界是 \(\sum[d_i\bmod c\not=0]\),可以应用上述方法证明它,对于所有度数 \(\geq c\) 的节点,我们对它做拆点,也就是任取 \(c\) 条边连在这个节点上,然后把这些边在原节点上删去,这样最后剩下的二分图最大度数一定 \(\leq c\),可以对它做边染色。

拆出来的点一定拥有 \([1,c]\) 中的所有颜色,原来的点会产生 \(1\) 的贡献,易知达到了答案下界,构造方案只需要记录每个点属于原先的哪个节点,然后把边染色对应上去即可。

四、总结

二分图中增广的思想特别重要,找有特殊性质(如最小值最大值)的元素构造的思想也值得借鉴。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 100005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,t,a[M],b[M],d[M],f[2005][1005];
void dfs(int u,int v,int x,int y)
{
	int to=f[v][x];
	f[u][x]=v;
	f[v][x]=u;
	if(!to)
	{
		f[v][y]=0;
		return ;
	}
	dfs(v,to,y,x);
}
signed main()
{
	n=read();m=read();k=read();
	for(int i=1;i<=k;i++)
	{
		a[i]=read();b[i]=n+read();
		d[a[i]]++;d[b[i]]++;
	}n+=m;
	for(int i=1;i<=n;i++) t=max(t,d[i]);
	for(int i=1;i<=k;i++)
	{
		int lx=1,ly=1;
		while(f[a[i]][lx]) lx++;
		while(f[b[i]][ly]) ly++;
		if(lx==ly)
		{
			f[a[i]][lx]=b[i];
			f[b[i]][lx]=a[i];
			continue;
		}
		dfs(a[i],b[i],lx,ly);
	}
	printf("%d\n",t);
	for(int i=1;i<=k;i++)
		for(int j=1;j<=t;j++)
			if(f[a[i]][j]==b[i])
			{
				printf("%d ",j);
				break;
			}
}
posted @ 2021-09-03 22:03  C202044zxy  阅读(95)  评论(0编辑  收藏  举报