Hdu 2767 Proving Equivalences【强联通-Kosaraju+思维】

时间:2022-07-12 09:50:04

Proving Equivalences

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5168    Accepted Submission(s): 1807

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

Source

NWERC 2008


题目大意:给出n个式子,m个推导关系,每个推导关系,表示公式a能够推出公式b,求还需要多少个式子,能够使得任意两个式子都能互相推导出来、


思路:

1、首先我们抽象化式子为节点,关系为边,将问题转化到图论上来,这里引入强连通的定义(有向图中):如果a能有路径到达b,并且b能有路径到达a,那么称a和b是强连通的,我们想将所有点之间的关系都弄成强连通的,其实也就是在求,需要引入多少条边,能够使得图的强连通分量是1.


2、我们首先来观察一个强连通分量为1的图【注意看箭头方向】:

图1:

Hdu 2767 Proving Equivalences【强联通-Kosaraju+思维】

对于有向边,我们有入度和出度的定义。在上图中,每个节点的入度和出度都是1.也同时说明,对于一个强连通分量为1的图,出度和入度两种度中,一定两种度都没有0度的存在。我们不妨在观察一个强连通分量为1的图:

Hdu 2767 Proving Equivalences【强联通-Kosaraju+思维】

其1的出度:2,入度2。

其2的出度:1,入度1。

其3的出度:1,入度1.

我们再观察一个强连通分量不是1的图:

Hdu 2767 Proving Equivalences【强联通-Kosaraju+思维】

其1的出度为2,入度为0

其2的出度为0,入度为1

其3的出度为0,入度为1

那么我们要将这个图变成强连通分量为1的图,我们不妨这样考虑:既然强连通分量为1的图有这样的特点:如果一个图是强连通分量为1的图,对于每个节点来说,一定没有任何一个点的出度或者入度有0的情况。那么我们的任务也就明确了,对于所有节点度的处理,使得没有0度的存在。

那么我们对上图再进行分析,这里设出度为0的节点数为a,设入度为0的节点数为b,其实我们如果找到了a和b的值,搞定最大值,其实也就搞定了这个问题。


那么最终得到这样的结论:设定output=max(a,b),假设output=a,那么就对出度为0的节点,一共连a条边,这些边都从出度为0的节点出发,到应该到的地方去。最终一定能使得图的出度和入度没有度为0的节点存在,同理,如果output=b,也能够达成目标。


3、既然得到了结论,下一个动作就是做题啦~,因为图是会存在有向环的,所以我们使用Kosaraju算法来求出强连通分量,然后对所有属于一个强连通分量的点缩点看成一个点进行染色,然后统计入度出度,得到结果。


AC代码:

[cpp]  view plain  copy  print ?
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. #include<algorithm>  
  4. using namespace std;  
  5. int head[100000];  
  6. int head2[100000];  
  7. struct EdgeNode  
  8. {  
  9.     int from;  
  10.     int to;  
  11.     int next;  
  12. }e[300000],ee[300000],se[300000];  
  13. int n,m,cont,cont2,sig;  
  14. int in[1000000];  
  15. int out[1000000];  
  16. int vis[1000000];  
  17. int num[1000000];  
  18. int color[1000000];  
  19. void add(int from,int to)  
  20. {  
  21.     e[cont].from=from;  
  22.     e[cont].to=to;  
  23.     e[cont].next=head[from];  
  24.     head[from]=cont++;  
  25. }  
  26. void add2(int from,int to)  
  27. {  
  28.     ee[cont2].from=from;  
  29.     ee[cont2].to=to;  
  30.     ee[cont2].next=head2[from];  
  31.     head2[from]=cont2++;  
  32. }  
  33. void init()  
  34. {  
  35.     cont=0;  
  36.     cont2=0;  
  37.     memset(vis,0,sizeof(vis));  
  38.     memset(out,0,sizeof(out));  
  39.     memset(in,0,sizeof(in));  
  40.     memset(head,-1,sizeof(head));  
  41.     memset(head2,-1,sizeof(head2));  
  42.     memset(color,0,sizeof(color));  
  43.     memset(num,0,sizeof(num));  
  44. }  
  45. void Dfs(int u)  
  46. {  
  47.     vis[u]=1;  
  48.     for(int i=head[u];i!=-1;i=e[i].next)  
  49.     {  
  50.         int v=e[i].to;  
  51.         if(vis[v]==0)  
  52.         {  
  53.             Dfs(v);  
  54.         }  
  55.     }  
  56.     num[sig++]=u;  
  57. }  
  58. void Dfs2(int u)  
  59. {  
  60.     vis[u]=1;  
  61.     color[u]=sig;  
  62.     for(int i=head2[u];i!=-1;i=ee[i].next)  
  63.     {  
  64.         int v=ee[i].to;  
  65.         if(vis[v]==0)  
  66.         {  
  67.             Dfs2(v);  
  68.         }  
  69.     }  
  70. }  
  71. void Kosaraju()  
  72. {  
  73.     sig=1;  
  74.     memset(vis,0,sizeof(vis));  
  75.     for(int i=1;i<=n;i++)  
  76.     {  
  77.         if(vis[i]==0)  
  78.         {  
  79.             Dfs(i);  
  80.         }  
  81.     }  
  82.     sig=0;  
  83.     memset(vis,0,sizeof(vis));  
  84.     for(int i=n;i>=1;i--)  
  85.     {  
  86.         if(vis[num[i]]==0)  
  87.         {  
  88.             sig++;  
  89.             Dfs2(num[i]);  
  90.         }  
  91.     }  
  92.     if(sig==1)  
  93.     {  
  94.         printf("0\n");  
  95.         return ;  
  96.     }  
  97.     for(int i=1;i<=n;i++)  
  98.     {  
  99.         for(int k=head[i];k!=-1;k=e[k].next)  
  100.         {  
  101.             int u=i,v=e[k].to;  
  102.             if(color[u]!=color[v])  
  103.             {  
  104.                 out[color[u]]++;  
  105.                 in[color[v]]++;  
  106.             }  
  107.         }  
  108.     }  
  109.     int ruu=0;  
  110.     int chu=0;  
  111.     for(int i=1;i<=sig;i++)  
  112.     {  
  113.         if(in[i]==0)ruu++;  
  114.         if(out[i]==0)chu++;  
  115.     }  
  116.     printf("%d\n",max(ruu,chu));  
  117. }  
  118. int main()  
  119. {  
  120.     int t;  
  121.     scanf("%d",&t);  
  122.     while(t--)  
  123.     {  
  124.         init();  
  125.         scanf("%d%d",&n,&m);  
  126.         for(int i=0;i<m;i++)  
  127.         {  
  128.             int x,y;  
  129.             scanf("%d%d",&x,&y);  
  130.             add(x,y);  
  131.             add2(y,x);  
  132.         }  
  133.         Kosaraju();  
  134.     }  
  135. }