CF1240F Football

复盘 pb 讲的题,来写篇题解造福社会

Description

给定一个无向图,把边染成 \([1,k]\) 的颜色(可以不染)。

对于一个点,设 \(S_i\) 为和它相邻的颜色为 \(i\) 的边数,要求 \(S\) 的极差不超过 \(2\) ,求一种满足的方案,使得尽可能多的边能够染色。

Solution

因为不确定所有所有边都能染上色,所以尝试一下。

先考虑 \(k=2\) 的时候,此时我们新建一个 0 点,然后连向所有度数为奇数的点,这样的话原图上就只有度数为偶的点了。

然后我们乱跑 dfs 。每个边跑一次,按顺序 0 1 0 1 染色,然后去掉与 0 连上的边。

因为如果经过一个非 0 点,入边和出边颜色必然不同;反之也只会差 1 (因为每个点至多与 0 连一条边),所以此时 \(S\) 的极差不超过 1 。

现在再考虑 \(k>2\) 的时候:

因为我们掌握了把两种颜色数量差控制在了 1 以内的技能,所以我们可以大胆地在图上乱染色,然后每次把极差最大的两种颜色调整一下。(但是不会证明。。)

所以目前看来,所有边都能被染上色,于是 \(w_i\) 没用,甩掉。

现在再考虑考虑正解会怎么做:

好像直接在原图上做没法进行什么改变,所以我们选择拆点,对于每条边 \((u,v)\) ,我们将它改变成 \((u,v+n)\)

这样我们就得到了一个二分图,问题也就转换到了,让每个点的 \(S\) 的极差不超过 \(1\) ,这样对于 \(i\)\(i+n\) 合在一起也满足题意。

然后发现好像还是不太能做,于是对于每个点,设度数为 \(deg_i\) ,令 \(a\cdot k+b=deg_i (b<k)\) ,将其拆成 \(a+1\) 个点。

然后现在对于没有染色的边对应的两点 \((u,v)\) ,分别选择两种没有使用过的颜色 \((c_i,c_j)\)\(u\) 没填过 \(c_i\)\(v\) 没填过 \(c_j\) ),现在分两种情况:

  1. \(c_i=c_j\) 肯定合法,直接填就是了。

  2. \(c_i\neq c_j\) 先把这条边填成 \(c_i\) ,然后找 \(v\) 的边有没有颜色是 \(c_i\) 的:如果有,把这条边改成 \(c_j\) ,然后从连向的点继续找;如果没有,那么就合法了。

可能存在环,连回 \(u\) 去了,再来考虑环的情况:

因为这是二分图,所以所有环都是偶环,又因为按照刚刚的填法,连向 \(u\) 的一定是要是一条颜色为 \(c_i\)

又考虑到每次操作完一定是合法的,所以这就违背了前提条件,所以不存在。

这么做的话,复杂度是 \(O(n\cdot m^2)\) 的,但是因为每条边最多遍历整个图,但实操的时候远达不到这个上界,所以跑起来时间是 ok 的。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+10;
int n,m,k,w,deg[N],poi[N],now;
int nxt[N][N],edge[N][N],col[N];
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
	return s*w;
}
inline void modify(int u,int co1,int co2){
	swap(nxt[u][co1],nxt[u][co2]);
	swap(edge[u][co1],edge[u][co2]);
	col[edge[u][co2]]=co2;//更换颜色
	if(nxt[u][co2])modify(nxt[u][co2],co2,co1);
}
int main(){
	n=read();m=read();k=read();
	for(int i=1;i<=n;++i)w=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read()+n,co1=0,co2=0;
		if(deg[u]%k==0)poi[u]=++now;
		if(deg[v]%k==0)poi[v]=++now;
		++deg[u];++deg[v];u=poi[u];v=poi[v];
		for(int j=1;j<=k;++j)if(!nxt[u][j]&&!nxt[v][j]){co1=j;break;}
		if(!co1){
			for(int j=1;j<=k;++j)if(!nxt[u][j]){co1=j;break;}
			for(int j=1;j<=k;++j)if(!nxt[v][j]){co2=j;break;}
			modify(v,co1,co2);
		}
		nxt[u][co1]=v;nxt[v][co1]=u;
		edge[u][co1]=edge[v][co1]=i;
		col[i]=co1;
	}
	for(int i=1;i<=m;++i)printf("%d\n",col[i]);
	return 0;
}
posted @ 2021-08-24 21:32  Illusory_dimes  阅读(88)  评论(0编辑  收藏  举报