hdu4635 有向点双

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

题意:给n个点m条边的有向图,问最多还能添加多少条边,让此图还不能形成强连通图;

分析:tarjan缩点后,我们知道要是点双的个数是1那么肯定不符合条件,就直接输出-1;

   那么剩下的情况就是,我们挑出一个点双,让剩余的点双全连通起来,当前点的点双也全连通起来,就剩下俩个点双,俩个点双之间也要尽可能连边;

   我们假设当前点双为 i ,剩下的点双 为 j (已经全连通过了),那么要是一开始 j 中的俩个点双分为对 i 有入边和出边,那么 i 和 j 就肯定能连通,就不满足条件了,所以我们要挑那些要么入度为0要么出度为0要么度数为0的点点双来考虑;

   对于每一个点双,答案贡献有三部分:1、当前点双 i 还能添加多少条边达到全连通;

                    2、剩下点双 j 还能添加多少条边达到全连通;

                    3、i 和 j 之间还能添加多少条边让i 和 j 不连通(i 和 j 之间的边要么全是出边要么全是入边)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int M=1e5+5;
vector<int>g[M];
int dfn[M],low[M],sta[M],cmp[M],in[M],out[M],n,m,top,tot,cnt;
bool vis[M];
ll sum[M],du[M],line[M];
void tarjan(int u){
    dfn[u]=low[u]=++cnt;
    sta[++top]=u;
    vis[u]=true;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        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]){
        cmp[u]=++tot;
        vis[u]=false;
        int len=top;
        while(sta[top]!=u){
            cmp[sta[top]]=tot;
            vis[sta[top--]]=false;
        }
        top--;
        sum[tot]=len-top;
    }
}
void init(){
    tot=top=cnt=0;
    for(int i=0;i<=n;i++)
        g[i].clear(),cmp[i]=0,low[i]=0,dfn[i]=0,out[i]=0,in[i]=0,line[i]=0,du[i]=0,sum[i]=0;
}
int main(){
    int t;
    scanf("%d",&t);
    for(int p=1;p<=t;p++){
        scanf("%d%d",&n,&m);
        init();
        for(int u,v,i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            g[u].pb(v);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
        printf("Case %d: ",p);
        if(tot==1){
            puts("-1");
            continue;
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<g[i].size();j++){
                int v=g[i][j];
                if(cmp[i]!=cmp[v]){
                    out[cmp[i]]++;
                    in[cmp[v]]++;
                    du[cmp[i]]++;
                    du[cmp[v]]++;
                }
                else line[cmp[i]]++;
            }
        for(int i=1;i<=tot;i++){
            ll nownum=1ll*(n-sum[i]);
            ll sum1=nownum*(nownum-1);///外面的连通块的总边数; 
            ll sum2=1ll*(m-line[i]-du[i]);///外面的连通块已经连的边数;
            ll sum3=sum[i]*nownum-du[i];///当前点可以再和外面连多少边;
            ll sum4=sum[i]*(sum[i]-1)-line[i];///当前连通块里还可以连的边 
        //    cout<<sum1-sum2<<"@@"<<sum3<<"@@"<<sum4<<endl;
            if(!du[i]||!in[i]||!out[i]){
                ans=max(ans,(sum1-sum2)+sum3+sum4);
            //    ans=max(ans,sum1-(m-line[i]-du[i])+sum[i]*nownum-du[i]);
            }
        }
        printf("%lld
",ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/12427730.html