hdu 4635 Strongly connected Kosaraju/Tarjan求强联通分量
题意: 给定N个顶点M条边的有向图。问,在保证这个图不是强连通图的情况下,最多可以增加多少条有向边。如果原图本身就是连通的,则输出-1。
分析:原图由多个强连通分量构成,要加边尽可能尽可能最多,然后图不是强连通的,我可以这么考虑加边的步骤:
- 在每一个强连通分量中继续增加边, 一直到不能再增加(加满整个强连通分量),可以让原图依旧不是强连通。
- 加完边之后,我们再来考虑缩点构成的DAG图。假设DAG中有K个顶点,那么,我可以选出(K-1)个缩点,在他们之间加边变成一个强连通分量,这样保证整个图中还剩下两个强连通分量。
- 对于剩下的两个强连通分量,我还可以加边,就是单向加边,依旧保证了图不是强连通图。
总结一下上面的步骤:可以把第一二步合并,就是原图中的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;
}