hdu 4635 Strongly connected Kosaraju/Tarjan求强联通分量大小

hdu 4635 Strongly connected  Kosaraju/Tarjan求强联通分量

题目链接:  hdu 4635 Strongly connected
题意: 给定N个顶点M条边的有向图。问,在保证这个图不是强连通图的情况下,最多可以增加多少条有向边。如果原图本身就是连通的,则输出-1。
分析:原图由多个强连通分量构成,要加边尽可能尽可能最多,然后图不是强连通的,我可以这么考虑加边的步骤:
  1. 在每一个强连通分量中继续增加边, 一直到不能再增加(加满整个强连通分量),可以让原图依旧不是强连通。
  2. 加完边之后,我们再来考虑缩点构成的DAG图。假设DAG中有K个顶点,那么,我可以选出(K-1)个缩点,在他们之间加边变成一个强连通分量,这样保证整个图中还剩下两个强连通分量。
  3. 对于剩下的两个强连通分量,我还可以加边,就是单向加边,依旧保证了图不是强连通图。
总结一下上面的步骤:可以把第一二步合并,就是原图中的K个强连通分量中,将他们分成两个部分,形成新的两个强连通分量X,Y,设这两个强连通分量中的顶点个数分别为x, y,有 x+y=N, 那么将这两个强连通分量内部连满边,总边数依次就是 x*(x-1)、 y*(y-1), 第三步,向A,B两个强连通分量中添加边,最多可以添加 x*y个。那么,图中总共的边数就是
x*(x-1)+y*(y-1)+x*y = x^2+y^2-(x+y)-xy = N^2-N-x*(N-x), 
显然 x (N-x)差距最大的时候,边数最多。 
那么,我们只需要从原图K个强连通分量中选出 含顶点最少并且 入度或者出度为0的一个强连通分量就好了。然后结果就是  N^2-N-x*(N-x)-M。为什么要缩点的入度或者出度为0呢?你画出DAG就知道了~~~
首先用 Kosaraju/Tarjan算法对图进行缩点,并同时保存原图中每个强连通分量的大小。然后对强连通分量按照大小排序。选出含顶点最少并且入度或者出度为0的一个强连通分量。然后就求出答案了。
/**
 *  Kosaraju 算法
 */
#include <set>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second
typedef __int64			LL;
typedef pair<int, int>	PII;

const int MAXN = 100000 + 5;
const int MAXM = 100000 + 5;

struct Edge {
	int v, next;
	Edge() {}
	Edge(int v, int next) : v(v), next(next) {}
};
struct Graph {
	Edge edge[MAXN];
	int head[MAXN], tot;
	void init() {
		tot = 0;
		memset(head, -1, sizeof(head));
	}
	void add_edge(int u, int v) {
		edge[tot] = Edge(v, head[u]);
		head[u] = tot++;
	}
} G1, G2;
int order[MAXN], Index;
int block[MAXN];
int cnt[MAXN], r[MAXN];
bool iFlag[MAXN], oFlag[MAXN];
bool vis[MAXN];
int T, N, M;
LL res;

bool cmp(const int& a, const int& b) {
	return cnt[a] < cnt[b];
}

void dfs1(int u) {
	int v;
	vis[u] = true;
	for (int i = G1.head[u]; ~i; i = G1.edge[i].next) {
		v = G1.edge[i].v;
		if (!vis[v]) dfs1(v);
	}
	order[++Index] = u;
}

void dfs2(int u, int k) {
	int v;
	vis[u] = true;
	block[u] = k;
	cnt[k] ++;
	for (int i = G2.head[u]; ~i; i = G2.edge[i].next) {
		v = G2.edge[i].v;
		if (!vis[v]) dfs2(v, k);
	}
}

int scc() {
	Index = 0;
	memset(vis, false, sizeof(vis));
	for (int i = 1; i <= N; i++) {
		if (!vis[i]) dfs1(i);
	}
	memset(vis, false, sizeof(vis));
	memset(cnt, 0, sizeof(cnt));
	memset(iFlag, false, sizeof(iFlag));
	memset(oFlag, false, sizeof(oFlag));
	int u, k = 0;
	for (int i = Index; i > 0; i--) {
		u = order[i];
		if (!vis[u]) {
			dfs2(u, ++k);
		}
	}
	return k;
}
set<PII> input;
int main() {
#ifndef ONLINE_JUDGE
	FIN;
#endif // ONLINE_JUDGE
	int u, v, cas = 0;
	scanf("%d", &T);
	while (T--) {
		G1.init();
		G2.init();
		input.clear();
		scanf("%d %d", &N, &M);
		for (int i = 0; i < M; i ++) {
			scanf("%d %d", &u, &v);
			int t1 = input.size();
			input.insert(make_pair(u, v));
			int t2 = input.size();
			if (t1 == t2) {
				i --;
				M --;
				continue;
			}
			G1.add_edge(u, v);
			G2.add_edge(v, u);
		}
		int k = scc();
		printf("Case %d: ", ++cas);
		if (k == 1) {
			puts("-1");
			continue;
		}
		for (int i = 1; i <= k; i++) r[i] = i;
		sort(r + 1, r + k + 1, cmp);
		int x;
		for (set<PII>::iterator iter = input.begin(); iter != input.end(); iter++) {
			u = (*iter).fst;
			v = (*iter).snd;
			if (block[u] == block[v]) continue;
			oFlag[block[u]] = true;
			iFlag[block[v]] = true;
		}
		for (int i = 1; i <= k; i++) {
			int j = r[i];
			if (!iFlag[j] || !oFlag[j]) {
				x = cnt[j];
				break;
			}
		}
		res = (LL)N * (LL)N - N - (LL) x * (LL)(N - x) - M;
		printf("%I64d\n", res);
	}
	return 0;
}
/**
 * Tarjan 算法
 */
#include <set>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second
typedef __int64			LL;
typedef pair<int, int>	PII;

const int MAXN = 100000 + 5;
const int MAXM = 100000 + 5;

struct Edge {
	int v, next;
	Edge() {}
	Edge(int v, int next) : v(v), next(next) {}
} edge[MAXN];
int head[MAXN], tot;
int low[MAXN], dfn[MAXN], stack[MAXN], belong[MAXN];
int cnt[MAXN], r[MAXN];
int Index, top;
int scc;
bool inStack[MAXN];
bool iFlag[MAXN], oFlag[MAXN];
int T, M, N;
set<PII> input;
bool cmp(const int& a, const int& b) {
	return cnt[a] < cnt[b];
}
void add_edge(int u, int v) {
	edge[tot] = Edge(v, head[u]);
	head[u] = tot++;
}
void Tarjan(int u) {
	int v;
	low[u] = dfn[u] = ++Index;
	stack[top++] = u;
	inStack[u] = true;
	for (int i = head[u]; ~i; i = edge[i].next) {
		v = edge[i].v;
		if (!dfn[v]) {
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		} else if (inStack[v] && low[u] > dfn[v]) {
			low[u] = dfn[v];
		}
	}
	if (low[u] == dfn[u]) {
		scc ++;
		do {
			v = stack[--top];
			inStack[v] = false;
			belong[v] = scc;
			cnt[scc] ++;
		} while (v != u);
	}
}
void init() {
	input.clear();
	scc = top = Index = tot = 0;
	memset(head, -1, sizeof(head));
	memset(inStack, false, sizeof(inStack));
	memset(cnt, 0, sizeof(cnt));
	memset(dfn, 0, sizeof(dfn));
	memset(iFlag, false, sizeof(iFlag));
	memset(oFlag, false, sizeof(oFlag));
}
int main() {
#ifndef ONLINE_JUDGE
	FIN;
#endif // ONLINE_JUDGE
	int u, v, cas = 0;
	scanf("%d", &T);
	while (T--) {
		init();
		scanf("%d %d", &N, &M);
		for (int i = 0; i < M; i ++) {
			scanf("%d %d", &u, &v);
			PII e = make_pair(u, v);
			if (input.find(e) == input.end()) {
				input.insert(e);
				add_edge(u, v);
			} else {
				i --;
				M --;
			}
		}
		printf("Case %d: ", ++cas);
		for (int i = 1; i <= N; i++) {
			if (!dfn[i]) Tarjan(i);
		}
		if (scc == 1) {
			puts("-1");
			continue;
		}
		for (set<PII>::iterator iter = input.begin(); iter != input.end(); iter++) {
			u = (*iter).fst, v = (*iter).snd;
			if (belong[u] != belong[v]) {
				oFlag[belong[u]] = true;
				iFlag[belong[v]] = true;
			}
		}
		for (int i = 1; i <= scc; i++) r[i] = i;
		sort(r + 1, r + scc + 1, cmp);
		int x;
		for (int i = 1, j; i <= scc; i++) {
			j = r[i];
			if (!iFlag[j] || !oFlag[j]) {
				x = cnt[j];
				break;
			}
		}
		LL res = (LL)N * N - N - (LL)x * (N - x) - M;
		printf("%I64d\n", res);
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值