题目
思路
由于这是一个二分图,先对其染色为黑、白。染色后,黑色点内部无边,白色点内部无边,故三个白色点是可以任意选取的。黑色点同理。
除去此二情况,只可能是 “一黑两白” 或者 “一白两黑” 。若二者均存在,则将其替换为 “三白” 与 “三黑” ,于是我们认为只有其中一种。若其中一种出现三次,亦可以将其替换为若干个 “三白” 与若干个 “三黑” 。
现在考虑整张图内白色点的个数,若其模 3 3 3 为 1 1 1 ,则一定是一个 “一白两黑” 或两个 “一黑两白” ,剩下的组成 “三黑” 与 “三白” 。(注意我前面所说,二者只能有一种,且不超过三个。)
对于 “一白两黑” ,很好处理,只需要看是否存在一个白点,其度数不超过黑点的数量减 2 2 2 。因为这样便一定有至少两个黑点与该白点无边,将其配对。
对于两个 “一黑两白” ,同样好处理,枚举一个黑点,其度数不超过白点的数量减 2 2 2 ,道理同上。两个黑点不会共用白点,因为边是双向的,若如此,则 “一白两黑” 已然存在。
找到了关键点就可以暴力找到与他匹配的点了。复杂度?我忘了,好像是 O ( n ) \mathcal O(n) O(n) 。
至于联通块数量的讨论,还有些可以说的。超过三个联通块时,先乱连,归约为三个。
我们可以想办法让黑白均为三的倍数——无非就是要选出 “一黑两白” 或 “一白两黑” 。
讨论大小为 1 1 1 的联通块——也就是单独的点——的个数(因为其他联通块必然黑白均有,不妨将其抽象为一白一黑的):
- 若为 1 1 1 ,则总能找到 “一黑两白” 与 “一白两黑” 。
- 若为 2 2 2 ,细致分类一下。
-
- 若这两个单独的点同色,不妨设其颜色为黑,则 “一白两黑” 已然存在。如果我们想凑出 “一黑两白” ,显然白点均与第三个联通块中,与这两个点中任意一个搭配即可。
-
- 若这两个点不同色,那么 “一黑两白” 和 “一白两黑” 都存在了。
- 若为 3 3 3 ,显然可以……三个单独的点可还行。
我们都能做到。结束。
两个联通块时,有两种本质不同的染色方案,都试一试就行了。
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
template < typename T >
void getMax(T&a,const T&b){if(a<b)a=b;}
template < typename T >
void getMin(T&a,const T&b){if(b<a)a=b;}
const int MaxN = 100015;
namespace ufs{
int fa[MaxN];
void init(int n){
for(int i=1; i<=n; ++i)
fa[i] = i;
}
int find(int a){
if(fa[a] != a)
fa[a] = find(fa[a]);
return fa[a];
}
bool linked(int a,int b){
return find(a) == find(b);
}
void link(int a,int b){
fa[find(a)] = find(b);
}
}
struct Edge{
int to, nxt;
Edge(int T=0,int N=0){
to = T, nxt = N;
}
} edge[MaxN<<1];
int head[MaxN], cntEdge, deg[MaxN];
void addEdge(int a,int b){
edge[cntEdge] = Edge(b,head[a]);
head[a] = cntEdge ++;
edge[cntEdge] = Edge(a,head[b]);
head[b] = cntEdge ++;
++ deg[a], ++ deg[b];
}
int color[MaxN], tmp[MaxN];
void colour(int x){
++ tmp[color[x]];
for(int i=head[x]; ~i; i=edge[i].nxt)
if(color[edge[i].to] == 0){
color[edge[i].to] = 3-color[x];
colour(edge[i].to);
}
}
int n, m, ans[MaxN];
void match(){
int ppl = 1, xez = 0;
for(int i=1; i<=n; ++i)
if(color[i] == 1 && !ans[i]){
ans[i] = ppl, ++ xez;
if(!(xez%=3)) ++ ppl;
}
for(int i=1; i<=n; ++i)
if(color[i] == 2 && !ans[i]){
ans[i] = ppl, ++ xez;
if(!(xez%=3)) ++ ppl;
}
puts("YES");
for(int i=1; i<=n; ++i)
printf("%d ",ans[i]);
exit(0);
}
bool xyx[MaxN];
void check(){
if(tmp[1]%3 == 0) match();
if(tmp[2]%3 == (tmp[1]+1)%3){
for(int i=1; i<=n; ++i)
color[i] = 3-color[i];
swap(tmp[1],tmp[2]);
}
if(tmp[1]%3 == (tmp[2]+1)%3){
for(int i=1; i<=n; ++i)
if(color[i] == 2)
if(deg[i] < tmp[1]-1){
ans[i] = n/3;
int sysy = 2;
for(int j=head[i]; ~j; j=edge[j].nxt)
xyx[edge[j].to] = 1;
for(int j=1; j<=n; ++j)
if(!xyx[j] && color[j] == 1){
ans[j] = n/3;
if(!(--sysy)) break;
}
match();
}
int sxy = 0;
for(int i=1; i<=n; ++i)
if(color[i] == 1)
if(deg[i] < tmp[2]-1)
++ sxy; // 2* 1+2+2
if(sxy >= 2){
int sysy = 2;
for(int i=1; i<=n; ++i)
if(color[i] == 1)
if(deg[i] < tmp[2]-1){
for(int j=1; j<=n; ++j)
xyx[j] = false;
for(int j=head[i]; ~j; j=edge[j].nxt)
xyx[edge[j].to] = true;
int lqy = 2;
ans[i] = n/3+sysy-2;
for(int j=1; j<=n; ++j)
if(!xyx[j] && color[j] == 2){
ans[j] = n/3+sysy-2;
if(!(--lqy)) break;
}
if(!(--sysy)) break;
}
match();
}
}
}
int main(){
n = readint(), m = readint();
ufs::init(n); int tot = n;
for(int i=1; i<=n; ++i)
head[i] = -1;
for(int a,b; m; --m){
a = readint(), b = readint();
addEdge(a,b);
if(!ufs::linked(a,b))
-- tot, ufs::link(a,b);
}
int ok = 0;
if(tot >= 3){
for(int i=1; i<=n; ++i)
if(color[i] == 0){
color[i] = 1;
colour(i);
}
tot = 1;
}
if(tot == 2){
color[1] = 1, colour(1);
int root; // 另一个联通块
for(int i=1; i<=n; ++i)
if(color[i] == 0){
root = i; break;
}
for(int i=1; i<=n; ++i)
tmp[i+2] = color[i];
tmp[n+3] = tmp[1];
tmp[n+4] = tmp[2];
for(int d=1; d<3; ++d){ // 做两次
tmp[1] = tmp[n+3];
tmp[2] = tmp[n+4];
for(int i=1; i<=n; ++i)
color[i] = tmp[i+2];
color[root] = d, colour(root);
check();
} // 做两次
}
if(tot == 1){
if(color[1] == 0)
color[1] = 1, colour(1);
check();
}
puts("NO");
return 0;
}
后记
Z X Y \tt ZXY ZXY 大佬说,两个联通块可能不需要翻转。我太弱了,我也不知道是不是这样的。
然后 X E Z \tt XEZ XEZ 大佬不翻转颜色过掉了。啊这……