http://acm.hdu.edu.cn/showproblem.php?pid=4635
问:最多加多少条边,使得原图不是强连通图
正向考虑有困难,不妨反向思考,既最少去掉几条边使得原图不是强连通。
总边数sum=n*(n-1)时肯定是强连通,已经给了m条边,sum-=m
这时把已经强连通的部分进行缩点,对于缩好的点我们把他们分成两部分,保证其中一部分到另一部分没有边(这两部分不强连通),再把sum减去两部分能构成所有的边数,取最大值即为答案
具体做时枚举每个小强连通块,找到num[i]*(n-num[i])最小的情况(num[i]为小强连通块点数),其中必须出度或入度为0的连通块才可以被选择
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <queue> #include <map> using namespace std ; const int INF=0xfffffff ; struct node { int s,t,nxt ; }e[4000005] ; int n,m,idx,ans,tp,cnt,num[100005],IN[100005],OUT[100005],dfn[100005],vis[100005],low[100005],head[100005],st[100005],belong[100005] ; void add(int s,int t) { e[cnt].s=s ; e[cnt].t=t ; e[cnt].nxt=head[s] ; head[s]=cnt++ ; } void tarjan(int u) { dfn[u]=low[u]=++idx ; vis[u]=1 ; st[++tp]=u ; int v ; for(int i=head[u] ;i!=-1 ;i=e[i].nxt) { v=e[i].t ; if(!dfn[v]) { tarjan(v) ; low[u]=min(low[u],low[v]) ; } else if(vis[v]) low[u]=min(low[u],dfn[v]) ; } if(dfn[u]==low[u]) { ans++ ; while(1) { v=st[tp--] ; vis[v]=0 ; belong[v]=ans ; num[ans]++ ; if(v==u)break ; } } } void sc() { memset(vis,0,sizeof(vis)) ; memset(dfn,0,sizeof(dfn)) ; memset(num,0,sizeof(num)) ; idx=tp=ans=0 ; for(int i=1 ;i<=n ;i++) if(!dfn[i]) tarjan(i) ; } int main() { int T ; scanf("%d",&T) ; for(int cas=1 ;cas<=T ;cas++) { cnt=0 ; memset(head,-1,sizeof(head)) ; scanf("%d%d",&n,&m) ; for(int i=0 ;i<m ;i++) { int s,t ; scanf("%d%d",&s,&t) ; add(s,t) ; } sc() ; if(ans==1) { printf("Case %d: -1 ",cas) ; continue ; } memset(IN,0,sizeof(IN)) ; memset(OUT,0,sizeof(OUT)) ; for(int u=1 ;u<=n ;u++) { for(int i=head[u] ;i!=-1 ;i=e[i].nxt) { int tt=e[i].t ; if(belong[tt]==belong[u])continue ; IN[belong[tt]]++ ; OUT[belong[u]]++ ; } } int ret=-1 ; for(int i=1 ;i<=ans ;i++) { if(!IN[i] || !OUT[i]) ret=max(ret,n*(n-1)-m-num[i]*(n-num[i])) ; } printf("Case %d: %d ",cas,ret) ; } return 0 ; }