【HDOJ4635】【Tarjan缩点+思维】【经典】
http://acm.hdu.edu.cn/showproblem.php?pid=4635
Strongly connected
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3821 Accepted Submission(s): 1510
Problem Description
Give a simple directed graph with N nodes and M edges. Please tell me the maximum number of the edges you can add that the graph is still a simple directed graph. Also, after you add these edges, this graph must NOT be strongly connected.
A simple directed graph is a directed graph having no multiple edges or graph loops.
A strongly connected digraph is a directed graph in which it is possible to reach any node starting from any other node by traversing edges in the direction(s) in which they point.
A simple directed graph is a directed graph having no multiple edges or graph loops.
A strongly connected digraph is a directed graph in which it is possible to reach any node starting from any other node by traversing edges in the direction(s) in which they point.
Input
The first line of date is an integer T, which is the number of the text cases.
Then T cases follow, each case starts of two numbers N and M, 1<=N<=100000, 1<=M<=100000, representing the number of nodes and the number of edges, then M lines follow. Each line contains two integers x and y, means that there is a edge from x to y.
Then T cases follow, each case starts of two numbers N and M, 1<=N<=100000, 1<=M<=100000, representing the number of nodes and the number of edges, then M lines follow. Each line contains two integers x and y, means that there is a edge from x to y.
Output
For each case, you should output the maximum number of the edges you can add.
If the original graph is strongly connected, just output -1.
If the original graph is strongly connected, just output -1.
Sample Input
3
3 3
1 2
2 3
3 1
3 3
1 2
2 3
1 3
6 6
1 2
2 3
3 1
4 5
5 6
6 4
Sample Output
Case 1: -1
Case 2: 1
Case 3: 15
题目大意:给一个简单有向图,在图中加边,问在保证图不强连通的情况下最多能加多少边。
题目分析:仔细考虑可知如果不连通,至少有两个强连通分量。而一个强连通分量最多有 N*(N-1)条边,所以这道题的最后答案应该是成为两个强连通分量。
总边数为 sum=(n1-1)*n1+(n2-1)*n2+(n2*n1),(其中n1+n2=n)由于已经存在了m条边,所以最后答案是ans=sum-m;
所以对于上述sum 的最大值求最大值可知应该使其中一个强连通分量的顶点数n1尽可能小,前提是这两个强连通分量之间没有强连通,即这两个强连通分量的入度或者出度为0。则在Tarjan求出各个强连通分量之后,再缩点统计各个强连通分量的入度和出度,最后找出顶点数最少的入度或者出度为0的强连通分量代入即可。
【PS:一定要记得使用边数m时,不要用while(m--),可能会因为下面需要使用m而无限wa。】
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<queue> 5 #include<stack> 6 #include<vector> 7 using namespace std; 8 const int maxn=100005;//边的最大值 9 const int maxn1=100005;//顶点最大值 10 struct edge{ 11 int from; 12 int to; 13 int next; 14 }EDGE[maxn]; 15 vector<int>vc[maxn1]; 16 int head[maxn1],dfn[maxn1],vis[maxn1],low[maxn1],col[maxn1],out[maxn1],in[maxn1],en[maxn1],stk[maxn1];//各个变量的意义可参照上篇博客 17 int edge_cnt=0,tot1=0,tot2=0,scc_cnt=0,tot0=0; 18 int n,m; 19 void add(int x,int y) 20 { 21 EDGE[edge_cnt].from=x; 22 EDGE[edge_cnt].to=y; 23 EDGE[edge_cnt].next=head[x]; 24 head[x]=edge_cnt++; 25 } 26 void Tarjan(int u) 27 { 28 low[u]=dfn[u]=++tot1;//注意tot1的初值必须是1【因为dfn必须为正数】,所以这里使用++tot1而不用tot1++; 29 vis[u]=1; 30 stk[++tot2]=u; 31 for(int i = head[u]; i != -1 ; i = EDGE[i].next) 32 { 33 if(!dfn[EDGE[i].to]){ 34 Tarjan(EDGE[i].to); 35 low[u]=min(low[u],low[EDGE[i].to]); 36 } 37 else if(vis[EDGE[i].to]){ 38 low[u]=min(low[u],low[EDGE[i].to]); 39 } 40 } 41 if(low[u]==dfn[u]){ 42 int xx; 43 scc_cnt++;//注意scc_cnt也是从1开始的,因为要染色,区别于为染色的0 44 do{ 45 xx=stk[tot2--]; 46 vc[scc_cnt].push_back(xx); 47 col[xx]=scc_cnt; 48 vis[xx]=0; 49 }while(xx!=u); 50 } 51 } 52 void INIT() 53 { 54 for(int i = 0 ; i < maxn1 ; i++) 55 vc[i].clear(); 56 edge_cnt=0,tot1=0,tot2=0,scc_cnt=0,tot0=0; 57 memset(head,-1,sizeof(head)); 58 memset(stk,0,sizeof(stk)); 59 memset(in,0,sizeof(in)); 60 memset(out,0,sizeof(out)); 61 memset(dfn,0,sizeof(dfn)); 62 memset(low,0,sizeof(low)); 63 memset(col,0,sizeof(col)); 64 } 65 void suodian()//缩点 66 { 67 for(int i = 0 ; i < edge_cnt ; i++) 68 { 69 if(col[EDGE[i].from]!=col[EDGE[i].to]) 70 { 71 in[col[EDGE[i].to]]++;//缩点 72 out[col[EDGE[i].from]]++; 73 } 74 } 75 } 76 int main() 77 { 78 int t; 79 scanf("%d",&t); 80 int case1=1; 81 while(t--) 82 { 83 INIT(); 84 scanf("%d%d",&n,&m); 85 int M=m; 86 while(M--) 87 { 88 int a,b; 89 scanf("%d%d",&a,&b); 90 add(a,b); 91 } 92 for(int i = 1 ; i <= n ; i++) 93 { 94 if(!dfn[i]){ 95 Tarjan(i); 96 } 97 } printf("Case %d: ",case1++); 98 if(scc_cnt==1) 99 { 100 printf("-1\n"); 101 } 102 else 103 { 104 suodian(); 105 int minn=-1; 106 for(int i = 1 ; i <= scc_cnt ; i++) 107 { 108 if(in[i]==0||out[i]==0) 109 { 110 int asd=vc[i].size(); 111 if(minn==-1||minn>asd) 112 { 113 minn=asd; 114 } 115 } 116 } 117 cout << (n-minn)*(n-minn-1)+minn*(minn-1)+minn*(n-minn)-m<<endl; 118 } 119 } 120 return 0; 121 }