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

来源:互联网 发布:淘宝上哪家卖红酒靠谱 编辑:程序博客网 时间:2024/06/01 10:19

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             secondtypedef __int64LL;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_JUDGEFIN;#endif // ONLINE_JUDGEint 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             secondtypedef __int64LL;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_JUDGEFIN;#endif // ONLINE_JUDGEint 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
原创粉丝点击