HDU 4635 Strongly connected (强连通分量+缩点)

<题目链接>

题目大意:

给你一张有向图,问在保证该图不能成为强连通图的条件下,最多能够添加几条有向边。

解题分析:

我们从反面思考,在该图是一张有向完全图的情况下,最少删去几条边能够使其不是强连通图。即,开始的时候,图的总边树为 n*(n-1),减去m条已有的边。然后把原图中所有的强连通块进行缩点,对于缩好的点,我们把其分成两部分,保证这两部分点不能够相互可达(即这两部分不是强连通),所以我们要减去一个部分到另一部分的所有同一方向的边,比如将连通块1到连通块2的所有边都删除,这样,这两部分点就不强连通,整张图也不是强连通图。那如何使删除的边最小呢?因为删除的边为cnt*(n-cnt),根据简单的数学常识,必然是cnt和(n-cnt)差值最大的时候,他们的乘积最小,所以我们只要记录所有连通块中点数最少的数量即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 typedef long long ll;
 8 const int N = 200010;
 9 const int INF = 0x3f3f3f3f;
10 
11 struct Edge{
12     int to,next;
13 }edge[N<<1];
14 
15 ll n,m,cnt,head[N];
16 ll tot,top,atype;
17 ll dfn[N],low[N],vis[N],stack[N],belong[N],indeg[N],outdeg[N],sum[N];
18 void init(){
19     cnt=0,tot=0,top=0,atype=0;;
20     memset(head,-1,sizeof(head));
21     memset(dfn,0,sizeof(dfn));
22     memset(low,0,sizeof(low));
23     memset(vis,0,sizeof(vis));
24     memset(belong,0,sizeof(belong));
25     memset(indeg,0,sizeof(indeg));
26     memset(outdeg,0,sizeof(outdeg));
27     memset(sum,0,sizeof(sum));
28 }
29 void addedge(int u,int v){
30     edge[++cnt].to=v,edge[cnt].next=head[u];
31     head[u]=cnt;
32 }
33 void Tarjan(int u){
34     dfn[u]=low[u]=++tot;
35     stack[top++]=u;
36     vis[u]=1;
37     for(int i=head[u];i!=-1;i=edge[i].next){
38         int v=edge[i].to;
39         if(!dfn[v]){
40             Tarjan(v);
41             low[u]=min(low[u],low[v]);
42         }else if(vis[v]){
43             low[u]=min(low[u],dfn[v]);
44         }
45     }
46     if(dfn[u]==low[u]){
47         atype++;
48         int v;
49         do{
50             v=stack[--top];
51             belong[v]=atype;  //将该强连通块缩点染色
52             sum[atype]++;    //统计该强连通块的点的数量  
53             vis[v]=0;
54         }while(u!=v);
55     }
56 }
57 
58 int main(){
59     int t,cases=0;
60     scanf("%d",&t);
61     while(t--){
62         scanf("%lld%lld",&n,&m);
63         init();
64         for(int i=0;i<m;i++){
65             int u,v;scanf("%d%d",&u,&v);
66             addedge(u,v);
67         }
68         for(int i=1;i<=n;i++)
69             if(!dfn[i])Tarjan(i);
70         if(atype==1){
71             printf("Case %d: -1
",++cases);
72             continue;
73         }
74         for(int u=1;u<=n;u++)
75             for(int i=head[u];i!=-1;i=edge[i].next){
76                 int v=edge[i].to;
77                 if(belong[u]!=belong[v]){    //统计每个连通块的初度和入度
78                     outdeg[belong[u]]++;
79                     indeg[belong[v]]++;
80                 }
81             }
82         ll ans=0,cnt=INF;
83         for(int i=1;i<=atype;i++)
84             if(indeg[i]==0||outdeg[i]==0)   //更新初度和入读为0的联通块的数量最小值,因为只需删除一个方向的边
85                 cnt=min(cnt,sum[i]);
86         ans=n*(n-1)-m-cnt*(n-cnt);     //n*(n-1)为有向完全图的所有边,m为原图已有的边,cnt*(n-cnt)为分成两部分后删除一个方向的边
87         printf("Case %d: %lld
",++cases,ans);
88     }
89     return 0;
90 }

2018-11-08

原文地址:https://www.cnblogs.com/00isok/p/9931942.html