#HDU 2767 (tarjan + 原理)

Problem Description

Consider the following exercise, found in a generic linear algebra textbook.

Let A be an n × n matrix. Prove that the following statements are equivalent:

1. A is invertible.
2. Ax = b has exactly one solution for every n × 1 matrix b.
3. Ax = b is consistent for every n × 1 matrix b.
4. Ax = 0 has only the trivial solution x = 0.

The typical way to solve such an exercise is to show a series of implications. For instance, one can proceed by showing that (a) implies (b), that (b) implies (c), that (c) implies (d), and finally that (d) implies (a). These four implications show that the four statements are equivalent.

Another way would be to show that (a) is equivalent to (b) (by proving that (a) implies (b) and that (b) implies (a)), that (b) is equivalent to (c), and that (c) is equivalent to (d). However, this way requires proving six implications, which is clearly a lot more work than just proving four implications!

I have been given some similar tasks, and have already started proving some implications. Now I wonder, how many more implications do I have to prove? Can you help me determine this?

Input

On the first line one positive number: the number of testcases, at most 100. After that per testcase:

* One line containing two integers n (1 ≤ n ≤ 20000) and m (0 ≤ m ≤ 50000): the number of statements and the number of implications that have already been proved.
* m lines with two integers s1 and s2 (1 ≤ s1, s2 ≤ n and s1 ≠ s2) each, indicating that it has been proved that statement s1 implies statement s2.

Output

Per testcase:

* One line with the minimum number of additional implications that need to be proved in order to prove that all statements are equivalent.

Sample Input

 

2 4 0 3 2 1 2 1 3

Sample Output

 

4 2

题目大意 : 输入一个有向图,至少需要加几条边,才能使这个图变成强连通图?

如下图所示 (有点丑哈哈)

 就以这张图来说,左边的1, 2, 3, 4是缩点后的图,(缩点就不用多说了)要想使一张图是强连通,不就是令他变成一个环吗,因为只有环才可以强连通,否则总有点的入度或出度为0,从而使这个点无法到达别的点或被别的点到达,那么我们把这个缺陷补上就好,我们把入度为0的点当作  “  尾  ”, 把出度为0的点当作   “  头  ”,头尾相连,就可以最优地解决问题了,还是挺形象的嘛,但是如果入度为0的个数和出度为0的个数不一样咋办?看看这一张图你就懂啦!

黑色的线代表缩点后的图,红色的线是新加的,不看红线,这时候这张图入度为0的点分别是1和4, 而出度为0的点只有3。这么想,要想强连通,就得对入度  or  出度为0的点进行加边操作,其实头和尾是一个道理,如果反向建图的话,原图的头就是反向建图后的尾,原图的尾就是反向建图后的头,所以取个数最大的那个就OK了!!

AC代码 :

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
const int maxn = 5e4 + 5;

struct node
{
    int v, next;
}e[maxn];
int dfn[maxn], low[maxn], suo[maxn], cnt, tot, scnt;
int head[maxn], in[maxn], out[maxn], n, m, T;
bool vis[maxn];
stack <int> st;
void init() {
    memset(e, 0, sizeof(e));
    memset(dfn, 0, sizeof(dfn));
    memset(suo, 0, sizeof(suo));
    memset(head, -1, sizeof(head));
    memset(low, 0, sizeof(low));
    memset(vis, 0, sizeof(vis));
    memset(in, 0, sizeof(in));
    memset(out, 0, sizeof(out));
    cnt = tot = scnt = 0;
}
void add (int from, int to) {
    e[++cnt].v = to;
    e[cnt].next = head[from];
    head[from] = cnt;
}
void tarjan(int x) {
    dfn[x] = low[x] = ++tot;
    vis[x] = 1;
    st.push(x);
    for (int i = head[x]; i != -1; i = e[i].next) {
        if (!dfn[e[i].v]) {
            tarjan(e[i].v);
            low[x] = min (low[x], low[e[i].v]);
        }
        else if (vis[e[i].v]) low[x] = min (low[x], dfn[e[i].v]);
    }
    if (dfn[x] == low[x]) {
        scnt++;
        int k;
        do {
            k = st.top();
            st.pop();
            vis[k] = 0;
            suo[k] = scnt;
        }
        while (k != x);
    }
}

int main()
{
    cin >> T;
    while (T--) {
        cin >> n >> m;
        if (!m) {cout << n << endl; continue;}
        init();
        for (int i = 0; i < m; i++) {
            int ui, vi;
            cin >> ui >> vi;
            add (ui, vi);
        }
        for (int i = 1; i <= n; i++) {
            if (!dfn[i]) tarjan(i);
        }
        if (scnt == 1) {cout << 0 << endl; continue;}
        for (int i = 1; i <= n; i++) {
            for (int j = head[i]; j != -1; j = e[j].next) {
                int u = suo[i];
                int v = suo[e[j].v];
                if (u != v) out[u]++, in[v]++;
            }
        }
        int sum1 = 0, sum2 = 0;
        for (int i = 1; i <= scnt; i++) {
            if (!in[i]) sum1++;
            if (!out[i]) sum2++;
        }
        cout << max (sum1, sum2) << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43851525/article/details/91437341