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\n",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\n",cas,ret) ;
    }
    return 0 ;
}
View Code