Codeforces 173 D. Deputies

問題

n頂点m辺からなる無向二部グラフが与えられる。
このグラフの頂点をちょうどk色で、それぞれの色が3回ずつ現れるように彩色したい。


そのような彩色は可能であるか、不可能ならNOを、
可能ならYESおよび、解を具体的に一つ出力せよ。

制約条件

n, m≦10^5

方針

まずnが3の倍数であることが必要条件。
二部グラフなので、適当にDFSして、頂点を二つの集合U, Vに分ける。

U が3の倍数ならば、三つずつ取って色を塗ればいいので終了。
U = 3x + 1, V = 3y + 2としてよい。

このとき、Uから一つ頂点uを選び、uに隣接していないVの頂点v, v'を二つ選ぶことができたら、|U|が3の倍数の場合に帰着できるので終了。


Vの側から二つ頂点v1, v2を選び、Uの側からv1につながっていない頂点u1, u1'および、
v2につながっていない頂点u2, u2'をとる。


これが取れないとき、明らかに条件を満たす彩色はない。
取れるとき、実はu1, u1', u2, u2'は全て互いに異なる。
なぜなら、それらのうちの二つがもし等しいとすると、「Uから一つ頂点uを選び、uに隣接していないVの頂点v, v'を二つ選ぶことができなかった」という場合分けに反するから。

ソースコード

int n, m;
vector<vi> e;
int na, v[100000], ans[100000];
int st1[100000], sz1, st2[100000], sz2;

void rec(int c, int cl){
	v[c] = 1;
	if(cl == 1) st1[sz1++] = c;
	else st2[sz2++] = c;
	rep(i, e[c].size()) if(!v[e[c][i]])
		rec(e[c][i], 3 - cl);
}
bool func(int *s, int s_, int *t, int t_, int cnt){
	int res[2], sz = 0;
	rep(i, s_) if((int)e[s[i]].size() <= t_ - 2){
		res[sz++] = s[i];
		if(sz == cnt) break;
	}
	if(sz < cnt) return 0;
	rep(i, sz){
		ans[res[i]] = na++;
		cnt = 0;
		rep(j, t_) if(!binary_search(all(e[res[i]]), t[j])){
			ans[t[j]] = na++;
			if(++cnt == 2) break;
		}
	}
	return 1;
}
void run()
{
	memset(ans, -1, sizeof(ans));
	cin >> n >> m;
	e.resize(n);
	rep(i, m){
		int a, b;
		cin >> a >> b;
		e[--a].pb(--b);
		e[b].pb(a);
	}
	rep(i, n) sort(all(e[i]));
	rep(i, n) if(!v[i]) rec(i, 1);
	if(n % 3){
		cout << "NO" << endl;
		return;
	}
	if(sz1 % 3){
		int *s = st1, *t = st2, s_ = sz1, t_ = sz2;
		if(sz1 % 3 == 2) swap(s, t), swap(s_, t_);
		if(!func(s, s_, t, t_, 1) && !func(t, t_, s, s_, 2)){
			cout << "NO" << endl;
			return;
		}
	}
	rep(i, sz1) if(ans[st1[i]] < 0) ans[st1[i]] = na++;
	rep(i, sz2) if(ans[st2[i]] < 0) ans[st2[i]] = na++;
	cout << "YES" << endl;
	rep(i, n) cout << ans[i]/3 + 1 << (i == n-1 ? "\n" : " ");
}