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\) ),现在分两种情况:
-
\(c_i=c_j\) 肯定合法,直接填就是了。
-
\(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;
}