HDU 4635 Strongly connected 解题报告

探讨了如何通过加边使非强连通有向图保持非强连通状态的同时达到最大边数的问题。利用Tarjan算法进行缩点处理,并在缩点后的图中寻找最优解。

题目

2013 暑假多校训练 4总结

题意:

一个有向图,如果是强连通就输出-1,如果不是,你可以不停加边,求使其依然不是强连通图的最多可加的边数。不能有重边或自环。

题解:

最优解形成的图一定是个完全图删边成有且只有一个点只有入边或出边,即边数为n*(n-1)-(n-1)。

但是可能图里含有强连通分量,所以tarjan缩点后,要在所有只有入边或出边的点里选一个点作为上述的特殊点。注意由于缩过点,比如当前点由t个点缩成,所以新图边数为n*(n-1)-t*(n-t),减去m就是解。


//Time:109ms
//Memory:5700KB
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <vector>
#define MAXN 300010
#define MOD 10007
#define INF 1000000007
using namespace std;
bool insta[MAXN];
int sta[MAXN],stop,now,n;
int in[MAXN],out[MAXN];
int dfn[MAXN],low[MAXN],cnt[MAXN],bel[MAXN],btop;
int he[MAXN],nex[MAXN],to[MAXN],top;
void add(int u,int v)
{
    to[top]=v;
    nex[top]=he[u];
    he[u]=top++;
}
void tarjan(int h)
{
    dfn[h]=low[h]=now++;
    sta[stop++]=h;
    insta[h]=1;
    for(int i=he[h];i!=-1;i=nex[i])
        if(dfn[to[i]]==0)
        {
            tarjan(to[i]);
            low[h]=min(low[h],low[to[i]]);
        }
        else  if (insta[to[i]])  low[h]=min(low[h],dfn[to[i]]);
    if(low[h]==dfn[h])
    {
        int tmp=0;
        do
        {
            insta[sta[--stop]]=0;
            bel[sta[stop]]=btop;
            ++tmp;
        }while(sta[stop]!=h);
        cnt[btop++]=tmp;
    }
}
int main()
{
    //freopen("/home/moor/Code/input","r",stdin);
    int ncase,m;
    long long ans;
    scanf("%d",&ncase);
    for(int h=1;h<=ncase;++h)
    {
        scanf("%d%d",&n,&m);
        memset(he,-1,sizeof(he));
        memset(dfn,0,sizeof(dfn));
        memset(insta,0,sizeof(insta));
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        now=1;
        top=0;
        stop=0;
        btop=0;
        for(int i=0;i<m;++i)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        ans=0;
        for(int i=1;i<=n;++i)
            if(!dfn[i])
                tarjan(i);
        printf("Case %d: ",h);
        if(btop==1)  printf("-1\n");
        else
        {
            long long tmp=(long long)n*(n-1);
            for(int i=1;i<=n;++i)
                for(int j=he[i];j!=-1;j=nex[j])
                    if(bel[to[j]]!=bel[i])
                        ++in[bel[to[j]]],++out[bel[i]];
            for(int i=0;i<btop;++i)
                if(in[i]==0||out[i]==0)
                    ans=max(ans,tmp-(n-cnt[i])*cnt[i]);
            cout<<ans-m<<'\n';
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值