<题目链接>
题目大意:
给你一张有向图,问在保证该图不能成为强连通图的条件下,最多能够添加几条有向边。
解题分析:
我们从反面思考,在该图是一张有向完全图的情况下,最少删去几条边能够使其不是强连通图。即,开始的时候,图的总边树为 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