hdu 4635 Strongly connected 强连通缩点

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

题意:给你一个n个点m条边的图,问在图不是强连通图的情况下,最多可以向图中添多少条边,若图为原来就是强连通图,输出-1即可;

思路:最后得到的图肯定分为两部分x和y,且两部分均为强连通分量,要么x的点到y的所有点有边,要么,从y的所有点到x的所有点有边;(其中只有入度或出度为0的点才可能成为x或y

则有         x+y=n 

答案为 ans = y*(y-1) + x*(x-1)+ y*x - m;

化简后 ans = n*n - n -n*x - x*x - m;

由以上化简试,x越小,ans值越大(答案最多为一百亿,long long 之)

#include "stdio.h"  //hdu 4635 强连通缩点
#include "string.h"
#include "vector"
#include "stack"
using namespace std;

#define N 201000
#define INF 0x3fffffff

long long n,m;
int time;
stack<int> q;
int dfn[N],low[N];
int MIN(int x,int y) { return x<y?x:y; }
long long MAX(long long x,long long y) { return x>y?x:y; }

struct node
{
    int x,y;
    int next;
}edge[2*N];
int idx,head[N];

bool vis[N];

void Init()
{
    idx=0;
    memset(head,-1,sizeof(head));
}

void Add(int x,int y)
{
    edge[idx].x = x;
    edge[idx].y = y;
    edge[idx].next = head[x];
    head[x] = idx++;
}

int countt;  //统计缩点个数
int num[N];  //统计每个缩点内的点的个数
int ru_du[N],chu_du[N];  //统计缩点的出,入度
int belong[N]; //标记点属于哪个缩点

void DFS(int x)
{
    int i,y;
    q.push(x);
    vis[x] = true;
    dfn[x] = low[x] = ++time;
    for(i=head[x]; i!=-1; i=edge[i].next)
    {
        y = edge[i].y;
        if(!dfn[y])
        {
            DFS(y);
            low[x] = MIN(low[x],low[y]);
        }
        else if(vis[y])   //强双连通图出现的新判断条件
            low[x] = MIN(dfn[y],low[x]);
    }
    if(low[x]==dfn[x])
    {
        int temp;
        countt++;
        while(1)
        {
            temp = q.top();
            q.pop();
            belong[temp] = countt;
            num[countt]++;
            vis[temp] = false;  //还原了~
            if(temp==x) break;
        }
    }
}

long long Solve()
{
    int i;
    int x,y;
    time = countt = 0;
    memset(dfn,0,sizeof(dfn));
    memset(num,0,sizeof(num));
    memset(belong,0,sizeof(belong));
    memset(vis,false,sizeof(vis));
    for(i=1; i<=n; ++i)
    {
        if(!dfn[i])
            DFS(i);
    }
    if(countt==1) return -1;
    memset(ru_du,0,sizeof(ru_du));
    memset(chu_du,0,sizeof(chu_du));
    for(i=0; i<idx; ++i)
    {
        x = edge[i].x;
        y = edge[i].y;
        if(belong[x]!=belong[y])
        {
            chu_du[belong[x]]++;
            ru_du[belong[y]]++;
        }
    }
    long long ans=0,mint;
    for(i=1; i<=countt; ++i)
    {
        if(chu_du[i]==0 || ru_du[i]==0){
            mint = num[i];
            ans = MAX(ans,n*n-n-mint*(n-mint)-m);
        }
    }
    return ans;
}

int main()
{
    int T,Case=0;
    int i;
    int x,y;
    scanf("%d",&T);
    while(T--)
    {
        Init();
        Case++;
        scanf("%lld %lld",&n,&m);
        for(i=0; i<m; ++i)
        {
            scanf("%d %d",&x,&y);
            Add(x,y);
        }
        printf("Case %d: ",Case);
        printf("%lld\n",Solve());
    }
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值