HDU - 4635 Strongly connected (强连通分量+思维)

链接:https://cn.vjudge.net/problem/HDU-4635

题意:给你一个有向简单图,问最多加多少边,加完之后该图仍不是强连通分量。如果已经是则输出-1。

思路:肯定是加到再加一条边就变成强连通分量为止。现在考虑怎么使加的边尽可能的多,也就是加完边后,边的总数尽可能的大。那么,最终的图肯定是在两个完全图之间,再加一些边组成。假设现在两个完全图的点数分别为x,y。可以得到:x+y=n;最后加的边肯定是一方(A)的所有点指向另一方(B)的所有点,而另一方(B)的所有点,没有一条边指向A的点。(也就是有一方出度为0或入度为0。)最后的边数也就是x*(x-1)+y*(y-1)+x*y,将x+y=n代入得,n*n-n-x*y。那么,加的边数就是n*n-n-x*y-m,要使此式的结果尽可能的大,那就是要让x*y尽可能的小。显然,x和y的差距越大,x*y的值越小,那么问题转化为,求含有最少点的强连通分量。注意,这个强连通分量要么出度为0,要么入度为0。可能有些小伙伴会问,那会不会所有的强连通分量的出入度都不为0,当然是不会的。如果所有的强连通分量的出入度都不为0,那么,他们合起来也是一个强连通分量啊。这种情况,已经被我们判断-1的时候排除掉了。详情看代码。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 1e5+10;
struct node
{
    int to,nxt;
}g[M];
int head[N],cnt;
int num[N],dfn[N],low[N],id,sta[N],top,color[N],cl;
int in[N],out[N];
bool vis[N];
ll n,m,ans;
void Init()
{
    cnt=top=id=cl=0;
    for(int i=1;i<=n;i++)
        head[i]=-1,in[i]=out[i]=dfn[i]=vis[i]=0;
}
void add(int u,int v)
{
    g[cnt].to=v; g[cnt].nxt=head[u]; head[u]=cnt++;
}
void tarjan(int u)
{
    int v;
    dfn[u]=low[u]=++id; sta[++top]=u; vis[u]=1;
    for(int i=head[u];i!=-1;i=g[i].nxt)
    {
        v=g[i].to;
        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])
    {
        color[u]=++cl;
        num[cl]=1;
        while(sta[top]!=u)
        {
            color[sta[top]]=cl; vis[sta[top--]]=0;
            num[cl]++;
        }
        vis[sta[top--]]=0;
    }
}
int main()
{
    int t,u,v,tt=0;
    ll temp;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        Init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        printf("Case %d: ",++tt);
        if(cl==1)
        {
            printf("-1\n");
            continue;
        }
        for(u=1;u<=n;u++)
            for(int i=head[u];i!=-1;i=g[i].nxt)
            {
                v=g[i].to;
                if(color[u]==color[v]) continue;
                out[color[u]]++,in[color[v]]++;
            }
        temp=1LL*n*n-n-m;
        ans=0;
        for(int i=1;i<=cl;i++)
            if(!in[i]||!out[i]) ans=max(ans,temp-1LL*(n-num[i])*num[i]);
        printf("%lld\n",ans);
    }

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值