1 /* 2 LA4287图论 3 有向图SCC 4 关键点: 5 1、事件抽象成图的节点 6 2、建立相似的模型(有向图SCC) 7 3、等效代替:缩点的思想 8 4、图论知识:非强连通转强连通
补充:a个缩点入度为0,b个出度为0,则添加max(a,b)条有向边,可以成为有向图的强连通分量
9 */ 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <math.h> 14 #include <ctype.h> 15 #include <string> 16 #include <iostream> 17 #include <sstream> 18 #include <vector> 19 #include <queue> 20 #include <stack> 21 #include <map> 22 #include <list> 23 #include <set> 24 #include <algorithm> 25 #define maxn 20100 26 using namespace std; 27 28 int pre[maxn] , low[maxn] , sccno[maxn] , dfs_clock , scc_cnt; 29 vector<int>G[maxn]; 30 stack<int>S; 31 32 void dfs(int u){ 33 pre[u] = low[u] = ++dfs_clock; 34 S.push(u); 35 int ecnt = G[u].size(); 36 for(int i=0;i<ecnt;i++){ 37 int v = G[u][i]; 38 if(!pre[v]){ 39 dfs(v); 40 low[u] = min(low[u] , low[v]); 41 } 42 else if(!sccno[v]){ 43 low[u] = min(low[u] , pre[v]); 44 } 45 } 46 if(low[u] == pre[u]){ 47 scc_cnt++; 48 for(;;){ 49 int x = S.top() ; S.pop(); 50 sccno[x] = scc_cnt; 51 if(x==u) break; 52 } 53 } 54 } 55 void find_scc(int n){ 56 dfs_clock = scc_cnt = 0; 57 memset(pre, 0 ,sizeof(pre)); 58 memset(sccno , 0, sizeof(sccno)); 59 for(int i=1;i<=n; i++){ 60 if(!pre[i]) dfs(i); 61 } 62 } 63 int solve(int n) 64 { 65 find_scc(n); 66 bool ind[maxn],oud[maxn];//缩点后的顶点的入度,出度是否大于0 67 memset(ind,0,sizeof(ind)); 68 memset(oud,0,sizeof(oud)); 69 for(int i=1;i<=n;i++){//枚举每条边 70 for(int j=0;j<G[i].size();j++){ 71 int u=i,v=G[i][j]; 72 if(sccno[u]!=sccno[v]) oud[sccno[u]]=ind[sccno[v]]=true; 73 } 74 } 75 int a=0,b=0; 76 for(int i=1;i<=scc_cnt;i++){ 77 if(!ind[i]) a++; 78 if(!oud[i]) b++; 79 } 80 if (scc_cnt==1) return 0;else return max(a,b); 81 //ps,scc_cnt==1时错误产生原因:上面的方法是运用必要条件来判断,破坏入度/出度为0的唯一情况,但是如果只有一个连通块,初始值就出现问题了 82 } 83 int main() 84 { 85 int n,m; 86 int t;cin>>t; 87 while(t--){ 88 cin>>n>>m; 89 for(int i=1;i<=n;i++) G[i].clear(); 90 for(int i=1;i<=m;i++){ 91 int u,v; 92 cin>>u>>v; 93 G[u].push_back(v); 94 } 95 cout<<solve(n)<<endl; 96 } 97 return 0; 98 }