多校第四场 Hdu 4635 强连通分量

题意:对于一个有向图,最多能加几条边依然使得图不变为强联通,如果原来就是强联通,输出-1,否则,输出最多能增加的边数

思路:最优解就是一个完全图删边的过程,删到有且只有一个点入度为0或者初度为0,所能增加的边s=n*(n-1)-(n-1) ,若图里存在强联通分量,那么就必须先缩点,把一个强联通分量看成一个点,重新构图,找到一点符合只有入度为0或者初度为0的“点”,这些点内连成完全图1,把剩下的不属于这个强联通分量的其他点组成另一部图也连成完全图2,然后把这两部图连起来(有向边),s=x*(x-1)+y*(y-1)+xy   (x、y)分别为图1图2的点的个数

代码;

#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
#include <stack>
using namespace std;
const int M=100005;
struct node
{
    int u,v;
    int next;
} edge[M];
int indu[M],outdu[M],head[M];
int belong[M],dfn[M],low[M],top,vis[M],num;
int n,m,cnt,st;
int Stack[M];
bool instack[M];
int benum[M];
void addEgde(int u,int v)
{
    edge[cnt].v=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++num;
    Stack[++top]=u;
    instack[u]=true;
    for(int i=head[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(instack[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        int x,sum=0;
        st++;
        do
        {
            x=Stack[top--];
            belong[x]=st;
            instack[x]=false;
            sum++;
        }
        while(x!=u);
        benum[st]=sum;//记录同一个强联通的点的个数
    }
}
int main()
{
    int t,u,v,g=1;
    scanf("%d",&t);
    while(t--)
    {
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(instack,0,sizeof(instack));
        memset(benum,0,sizeof(benum));
        memset(indu,0,sizeof(indu));
        memset(outdu,0,sizeof(outdu));
        st=top=num=cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&u,&v);
            addEgde(u,v);
        }
        for(int i=1; i<=n; i++)
            if(!dfn[i])
                tarjan(i);
        if(st==1)
        {
            printf("Case %d: -1\n",g++);
            continue;
        }
        for(int i=1; i<=n; i++)
        {
            for(int j=head[i]; j!=-1; j=edge[j].next)
            {
                if(belong[i]==belong[edge[j].v])continue;
                outdu[belong[i]]++;
                indu[belong[edge[j].v]]++;
            }
        }
        long long ans=0;
        for(int i=1; i<=st; i++)//连通分量
        {
            if(outdu[i]==0 || indu[i]==0)
            {
                int x=benum[i],y=n-x;
                ans=max(ans,1LL*x*(x-1)+1LL*y*(y-1)+1LL*x*y-m);
            }
        }
        printf("Case %d: %I64d\n",g++,ans);
    }
    return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值