HDOJ 4635: Strongly connected

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4635


题目大意:

给定一个有向图,

问至多加多少条边,

使得新图是非强连通的简单图。


算法:

与其算加多少边,不如算有多少边不能加。

很容易想到,最终的图是分成两个强连通分支的。

这两个强联通分支内部分别是完全图,强连通分支之间的边都是由某个SCC指向另一个SCC的,不可能两个方向都有。

所以,不能加的就是两个SCC之间另一个方向上的边,数量就是两个SCC中点数的乘积。

不难看出让两个SCC的点数差距越大越好。

那么首先进行SCC缩点。

缩点后,如果一个SCC出度入度均不为0,那么它最终肯定是不可能单独作为剩下的两个SCC之一了,因为最后两个SCC间的边都是沿同一方向的。

在所有出度或入度为0的SCC中,我们找一个点数最少的。

那么最终就是这个SCC在一边,其它所有的点在另一边,中间的边都是沿着同一方向(出或入)。


代码如下:

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <math.h>
#include <queue>
#include <vector>
#include <algorithm>
#include <stack>
#include <map>
using namespace std;

#define ll long long
#define inf 2e9
#define pii pair<int,int>
#define fr first
#define sc second

const int MAXN=110000;
stack<int> ss;
int dep[MAXN], low[MAXN], cot[MAXN];
int dex1[MAXN],dex2[MAXN],blk[MAXN];
vector<int> mm[MAXN];

void tarjan(int u, int p)
{
    int tmp=low[u]=dep[u]=(p==-1)?0:dep[p]+1;
    ss.push(u);
    for(int i=0; i<mm[u].size(); i++)
    {
        int v=mm[u][i];
        if(dep[v]==-1)
        {
            tarjan(v,u);
        }
        tmp=min(tmp,low[v]);
    }
    low[u]=tmp;
    if(low[u]==dep[u])
    {
        while(ss.top()!=u)
        {
            cot[u]++;
            blk[ss.top()]=u;
            ss.pop();
        }
        blk[ss.top()]=u;
        ss.pop();
        cot[u]++;
    }
}

int main(){
    int cas;
    scanf("%d", &cas);
    for(int T=1; T<=cas; T++)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        memset(dep, -1, sizeof(dep));
        memset(cot, 0, sizeof(cot));
        memset(dex1, 0, sizeof(dex1));
        memset(dex2, 0, sizeof(dex2));
        while(!ss.empty())
        {
            ss.pop();
        }
        for(int i = 0; i < n; i ++)
        {
            mm[i].clear();
        }
        for(int i = 0; i < m; i ++)
        {
            int u,v;
            scanf("%d%d", &u, &v);
            u--;
            v--;
            mm[u].push_back(v);
        }
        for(int i=0; i<n; i++)
        {
            if(dep[i]==-1)
            {
                tarjan(i,-1);
            }
        }
        for(int u=0; u<n; u++)
        {
            for(int i=0; i<mm[u].size(); i++)
            {
                int v=mm[u][i];
                if(blk[u]==blk[v])
                {
                    continue;
                }
                dex1[blk[u]]++;
                dex2[blk[v]]++;
            }
        }
        long long ans=-1;
        for(int i=0; i<n; i++)
        {
            if(cot[i]==0||cot[i]==n)
            {
                continue;
            }
            if(dex1[i]&&dex2[i])
            {
                continue;
            }
            ans=max(ans,(long long)n*(n-1)-(long long)cot[i]*(n-cot[i])-m);
        }
        printf("Case %d: %I64d\n",T,ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值