hdu 4635 Strongly connected (tarjan强连通分量)

题意:给你一个有向图,问在保证这个图无重边,无自环,且不是强连通图的情况下,最多可以添加多少条有向边

思路:分析可知,最终得到的图分为A,B两个部分,两部分各自为完全图,A中每个点都向B中每个点引一条有向边,B中每个点必定不存在通往A的有向边。这样可推得整个图的总边数为a*(a-1)+b*(b-1)+ab,需要添加的边数是a*(a-1)+b*(b-1)+ab-m。而a+b=n,化简得a*(a-1)+b*(b-1)+ab=n*n-n-a*b。ab=a*(n-a)=b*(n-b),若要让a*b尽量小,根据函数曲线,要求a和b尽量远离n/2。令b尽量小,则需要找到原图中入度为0或出度为0的强连通分量中点数最少的一个作为B部分,其余的作为A部分。

 

#include<iostream>
#include<stack>
#include<vector>
#include<cstring>
#include<string>
using namespace std;

const long long maxn=100001;
const long long INF=0x3f3f3f3f;

vector <long long> v[maxn];
stack  <long long> s;

long long n,m,t,T,ans,sum,minn,visit,numa,numb;
long long dfn[maxn],low[maxn],belong[maxn],stack[maxn],x[maxn],y[maxn],number[maxn],inn[maxn],outt[maxn];
bool instack[maxn];

void tarjan(long long x)
{
    long long y,k,temp;
    visit++;
    dfn[x]=low[x]=visit;
    instack[x]=true;
    s.push(x);
    for(int i=0;i<v[x].size();i++)
    {
        y=v[x][i];
        if(!dfn[y])
        {
            tarjan(y);
            if(low[y]<low[x])
                low[x]=low[y];
        }
        else if(instack[y] && dfn[y]<low[x])
            low[x]=dfn[y];
    }
   // cout<<"x="<<x<<"  dfn[x]="<<dfn[x]<<"  low[x]="<<low[x]<<endl;
    if(dfn[x]==low[x])
    {
        temp=0;
        if(!s.empty())
        {
            sum++;
            while(!s.empty())
            {
                temp++;
                k=s.top();
                instack[k]=false;
                belong[k]=sum;
                s.pop();
                if(k==x) break;
            }
            number[sum]=temp;
           // cout<<"number["<<sum<<"]="<<temp<<endl;
        }

    }
}

void solve()
{
    while(!s.empty())
        s.pop();
    visit=sum=0;
    minn=INF;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(inn,0,sizeof(inn));
    memset(outt,0,sizeof(outt));
    memset(belong,0,sizeof(belong));
    memset(instack,0,sizeof(instack));
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=m;i++)
    {
        if(belong[x[i]]==belong[y[i]]) continue;
        outt[belong[x[i]]]++;
        inn[belong[y[i]]]++;
    }
    for(int i=1;i<=sum;i++)
        if(inn[i]==0 || outt[i]==0)
            if(number[i]<minn)
                minn=number[i];
}

int main()
{
    cin>>T;
    t=0;
    while(T--)
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            v[i].clear();
        for(int i=1;i<=m;i++)
        {
            cin>>x[i]>>y[i];
            v[x[i]].push_back(y[i]);
        }
        solve();
        if(sum==1)
            cout<<"Case "<<++t<<": "<<-1<<endl;
        else
        {
            numa=n-minn;
            numb=minn;
            ans=numa*(numa-1)+numb*(numb-1)+numa*numb-m;
            cout<<"Case "<<++t<<": "<<ans<<endl;
     //       cout<<"min="<<minn<<endl<<"ans="<<ans<<endl;
        }
    }
    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值