hDU2767Equivalences【强连通缩点+添加多少条边可以使有向图强连通】

大意:

告诉你有n个点  m条单向边  问最少添加多少条边能将该图变成强连通

分析:

先用强连通进行缩点  缩成一个有向无环图 

然后我们考虑如何才能使 这个有向无环图变成一个强连通

我的第一反应是求最小路径覆盖  然后再首尾相连  结果时间复杂度承受不住

其实想到这里可以大胆猜想一下了   

刚刚提到了  首尾连接这个思想

我们只要统计有多少个头多少个尾  然后用尾去连接头就好了  答案就是首尾最大值
统计首尾也就是统计入度出度为0的点的个数

 

需要注意的是  若强连通缩点之后点缩成了一个  那么这个点本身就是一个强连通

 

代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <map>
  5 #define clr(x) memset(x, 0, sizeof(x))
  6 using namespace std;
  7 int min(int a, int b) {
  8     return a > b ? b : a;
  9 }
 10 
 11 const int maxn = 500005;
 12 const int maxm = 500005;
 13 
 14 struct Edge {
 15     int to, next;
 16 }e[maxm];
 17 int tot;
 18 int head[maxn];
 19 int id[maxn];
 20 
 21 void add(int u, int v) {
 22     e[tot].to = v;
 23     e[tot].next = head[u];
 24     head[u] = tot++;
 25 }
 26 int n, m;
 27 
 28 int s[maxn];
 29 int ins[maxn];
 30 int s_cnt;
 31 int id_cnt;
 32 int cnt;
 33 int dfn[maxn], low[maxn];
 34 void dfs(int u) {
 35     s[s_cnt++] = u;
 36     ins[u] = 1;
 37     dfn[u] = low[u] = cnt++;
 38     for(int i = head[u]; i; i = e[i].next) {
 39         int v = e[i].to;
 40         if(!dfn[v]) {
 41             dfs(v);
 42             low[u] = min(low[u], low[v]);
 43         } else if(ins[v]) {
 44             low[u] = min(low[u], dfn[v]);
 45         }
 46     }
 47     if(dfn[u] == low[u]) {
 48         int x;
 49         id_cnt++;
 50         do {
 51             x = s[--s_cnt];
 52             ins[x] = 0;
 53             id[x] = id_cnt;
 54         } while(x != u);
 55     }
 56 }
 57 
 58 void init() {
 59     clr(head);
 60     clr(dfn);
 61     clr(low);
 62     clr(id);
 63     clr(ins);
 64     cnt = 1;
 65     s_cnt = 1;
 66     id_cnt = 0;
 67     tot = 1;
 68 }
 69 int ind[maxn], outd[maxn];
 70 
 71 int main() {
 72     int t;
 73     scanf("%d",&t);
 74     int u, v;
 75     while(t--) {
 76         scanf("%d %d",&n, &m);
 77         init();
 78         for(int i = 1; i <= m; i++) {
 79             scanf("%d %d",&u, &v);
 80             add(u, v);
 81         }
 82         for(int i = 1; i <= n; i++) {
 83             if(!dfn[i]) {
 84                 dfs(i);
 85             }
 86         }
 87         if(id_cnt == 1) {
 88             printf("%d\n", 0);
 89             continue;
 90         }
 91         int ans1 = 0; int ans2 = 0;
 92         clr(ind); clr(outd);
 93         for(int i = 1; i <= n; i++) {
 94             for(int j = head[i]; j; j = e[j].next) {
 95                 int x = e[j].to;
 96                 if(id[i] != id[x]) {
 97                     ind[id[x]]++;
 98                     outd[id[i]]++;
 99                 }
100             }
101         }
102         for(int i = 1; i <= id_cnt; i++) {
103             if(ind[i] == 0) ans1++;
104             if(outd[i] == 0) ans2++;
105         } 
106         if(ans1 < ans2) ans1 = ans2;
107         printf("%d\n", ans1);
108     }
109     return 0;
110 }
111 
112     
View Code

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值