UVALive - 4287 - Proving Equivalences(强连通分量)

Problem   UVALive - 4287 - Proving Equivalences

Time Limit: 3000 mSec

Problem Description

 

Input

 

Output

 

Sample Input

2 4 0 3 2 1 2 1 3

Sample Output

4

2

 

题解:题意就是给出一个有向图,问最少添加几条有向边能够使得整张图强连通,Tarjan缩点是比较容易想到的,之后怎么办,要用到一个结论:如果图中有a个入度为零的点,b个出度为零的点,那么max(a, b)就是答案,这个东西不太容易严格证明(在一份ppt上看到说证明难,略。。。),但是形式上想一想还是挺对的。此外mark两个结论,这两个是很容易严格证明的:

  1、DAG中唯一出度为0的点一定可以由任意点出发到达。(证明:由于无环,因此所有点都要终止在出度为0的点)

  2、DAG中所有入度不为0的点一定可以由某个入度为0的点出发到达。(证明:由于无环,入度不为零的点逆着走一定终止在入度为0的点)

 

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 #define REP(i, n) for (int i = 1; i <= (n); i++)
  6 #define sqr(x) ((x) * (x))
  7 
  8 const int maxn = 20000 + 10;
  9 const int maxm = 30 + 10;
 10 const int maxs = 10000 + 10;
 11 
 12 typedef long long LL;
 13 typedef pair<int, int> pii;
 14 typedef pair<double, double> pdd;
 15 
 16 const LL unit = 1LL;
 17 const int INF = 0x3f3f3f3f;
 18 const LL mod = 1000000007;
 19 const double eps = 1e-14;
 20 const double inf = 1e15;
 21 const double pi = acos(-1.0);
 22 
 23 int n, m;
 24 vector<int> G[maxn];
 25 int dfs_clock, scc_cnt;
 26 int pre[maxn], sccno[maxn];
 27 stack<int> S;
 28 
 29 int dfs(int u)
 30 {
 31     S.push(u);
 32     int lowu = pre[u] = ++dfs_clock;
 33     for (auto v : G[u])
 34     {
 35         if (!pre[v])
 36         {
 37             int lowv = dfs(v);
 38             lowu = min(lowu, lowv);
 39         }
 40         else if (!sccno[v])
 41         {
 42             lowu = min(lowu, pre[v]);
 43         }
 44     }
 45     if (lowu == pre[u])
 46     {
 47         scc_cnt++;
 48         for (;;)
 49         {
 50             int t = S.top();
 51             S.pop();
 52             sccno[t] = scc_cnt;
 53             if (t == u)
 54                 break;
 55         }
 56     }
 57     return lowu;
 58 }
 59 
 60 void find_scc()
 61 {
 62     dfs_clock = scc_cnt = 0;
 63     memset(pre, 0, sizeof(pre));
 64     memset(sccno, 0, sizeof(sccno));
 65     for (int i = 0; i < n; i++)
 66     {
 67         if (!pre[i])
 68         {
 69             dfs(i);
 70         }
 71     }
 72 }
 73 
 74 int out[maxn], in[maxn];
 75 
 76 int main()
 77 {
 78     ios::sync_with_stdio(false);
 79     cin.tie(0);
 80     //freopen("input.txt", "r", stdin);
 81     //freopen("output.txt", "w", stdout);
 82     int T;
 83     cin >> T;
 84     while (T--)
 85     {
 86         memset(out, 0, sizeof(out));
 87         memset(in, 0, sizeof(in));
 88         cin >> n >> m;
 89         for (int i = 0; i < n; i++)
 90         {
 91             G[i].clear();
 92         }
 93         int u, v;
 94         for (int i = 0; i < m; i++)
 95         {
 96             cin >> u >> v;
 97             u--, v--;
 98             G[u].push_back(v);
 99         }
100         find_scc();
101         for (int u = 0; u < n; u++)
102         {
103             for (auto v : G[u])
104             {
105                 if (sccno[v] != sccno[u])
106                 {
107                     out[sccno[u]]++;
108                     in[sccno[v]]++;
109                 }
110             }
111         }
112         int a = 0, b = 0;
113         for (int i = 1; i <= scc_cnt; i++)
114         {
115             if (!out[i])
116                 a++;
117             if (!in[i])
118                 b++;
119         }
120         int ans = max(a, b);
121         if (scc_cnt == 1)
122             ans = 0;
123         cout << ans << endl;
124     }
125     return 0;
126 }

 

转载于:https://www.cnblogs.com/npugen/p/10751166.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值