agc006_f

题目叙述

给一个有向图,对于三个点如果 \(u\rightarrow v\)\(v\rightarrow w\)\(u,v,w\) 不一定两两不同),那么再连一条 \(w\rightarrow u\) 的边。求最终的总边数。图可能有自环。

题解

对每个弱连通块分别考虑。弱连通块定义为将有向边视为无向边的连通块。

将图用三种颜色红、黄、蓝染色。红色点只能向黄色点连边,黄色点只能向蓝色点连边,蓝色点只能向红色点连边。弱环定义为将所有边视为无向图形成的环。

如果不存在一个这样的染色方法,设块的大小为 \(x\),那么将产生 \(x^2\) 条边。因为这样必然会有一个弱环满足他不能被染色。现在每次将这个弱环的边 \(u\rightarrow v\)\(v\rightarrow w\) 的边缩为 \(w\rightarrow u\) ,那么这个弱环必将不断缩小,并且依旧不能用三种颜色染色。最终将会缩小成一个自环。一个自环将把整个环传染成一个完全图。

如果一个图存在这样的染色方法,并且三种颜色都用上了,那么这个图必然还是只能红色点黄色点连边,黄色点只能向蓝色点连边,蓝色点只能向红色点连边。只要这个图存在一个三元环 \(u\rightarrow v,v\rightarrow w,w\rightarrow u\) ,那么对于任意 \(x\)\(u\) 有边,那么必然会产生 \(x\rightarrow u,u\rightarrow w,w\rightarrow x\) 。所以这样下去对于任意红色点会与任意黄色点右边,任意黄色点会向任意蓝色点有边,任意蓝色点会向绿色点有边。这样会产生 \(r\cdot y+y\cdot b+b\cdot r\) 。其中 \(r,y,b\) 表示红色、黄色、蓝色点数量。

如果存在染色方法并且至多两种颜色用上了,可以发现不能连出任何新边。

这样做就行了。

实现

#include <cstdio>
using namespace std;
const int NN = 1e5 + 5, NM = 1e5 + 5;
int N, M;
struct E {
	int v, nxt, fx;
	E(int _v=0, int _nxt=0, int _fx=0) : v(_v), nxt(_nxt), fx(_fx) {}
};
struct Graph {
	int head[NN], totE;
	E edge[NM<<1];
	void AddE(int u, int v, int fx) {
		edge[++totE] = E(v, head[u], fx);
		head[u] = totE;
	}
} G;
int col[NN];
bool vis[NN];
int ys[] = {1, 2, 0};
int fanys[] = {2, 0, 1};
//0->1
//1->2
//2->0
int cnt[3], egnum = 0, cc;
bool Dfs(int u) {
	vis[u] = 1;
	cnt[col[u]] += 1;
	int hoho = 0;
	bool ret = 1;
	for (int p = G.head[u]; p; p = G.edge[p].nxt) {
		int v = G.edge[p].v;
		++hoho;
		if (vis[v]) {
			if (G.edge[p].fx == 1 && col[v] != ys[col[u]]) {
				ret = 0;
			}
			if (G.edge[p].fx == -1 && col[v] != fanys[col[u]]) {
				//注意第一个 G.edge[p].fx == -1的条件
				//以后限制能多写就多写
				ret = 0;
			}
			continue ;
		}
		if (G.edge[p].fx == 1)
			col[v] = ys[col[u]];
		else col[v] = fanys[col[u]];
		if (!Dfs(v)) //记得这句话
			ret = 0;
	}
	egnum += hoho;
	return ret;
}
int main() {
	scanf("%d%d", &N, &M);
	for (int i = 1; i <= M; ++i) {
		int u, v;
		scanf("%d%d", &u, &v);
		G.AddE(u, v, 1);
		G.AddE(v, u, -1);
	}
	long long ans = 0;
	for (int i = 1; i <= N; ++i) {
		if (!vis[i]) {
			cnt[0] = cnt[1] = cnt[2] = 0;
			egnum = 0; //初始化!
			bool jd = Dfs(i);
			int sum = cnt[0] + cnt[1] + cnt[2];
			//这里得 dfs完 cnt[0] cnt[1] cnt[2]才准确
			if (!jd) ans += (long long)sum * sum;
			else if (!cnt[0] || !cnt[1] || !cnt[2]) ans += egnum / 2;
			else ans += (long long)cnt[0] * cnt[1] +
						(long long)cnt[1] * cnt[2] +
						(long long)cnt[2] * cnt[0];
		}
	}
	printf("%lld\n", ans);
	return 0;
}
/*
5 4
1 2
2 3
4 5
5 5
*/

总结

  • 和三元环有关系的题考虑用三种颜色染色?
02-14 04:47