【Tarjan】HDU - 4635 - Strongly connected

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


题意:

在一张有向图上加尽可能多的边,使它不成为强连通图。输出能加的边的数量。


题解:

首先Tarjan缩点。考虑把图上的点分成两块,左边点的数量为n,右边点的数量为mn+m=tt为点的总数。

左边所有的点互相连接,一共有n*(n-1)条边;右边所有的点互相连接,一共有m*(m-1)条边。

左边的点与右边连一条有向边,一共有n*m条边。

所以总共能够构成n*(n-1)+m*(m-1)+n*m条边。

拆开化简就是t^{2}-t-n*m。要使这个值最大,就要是n*m尽可能地小。因为n+m=t,所以n*m最小的情况就是两者差值最大的情况。

所以我们可以找出所有入度为零或出度为零的点,找一个最小值代入为n。将最后构造出来的边数减去原来存在的边即是答案。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+7;
struct Edge{
    ll v,nxt;
    Edge(ll v=0,ll nxt=0):v(v),nxt(nxt){}
}e[N];
int in[N],ou[N];
ll t,n,m,u,v,cs;

ll p[N],edn;
void add(ll u,ll v){
    e[++edn]=Edge(v,p[u]);p[u]=edn;
}
ll dfn[N],low[N],num[N],st[N],scc[N],sccnum,idx,top;
void tarjan(ll u){
    dfn[u]=low[u]=++idx;
    st[++top]=u;
    for(ll i=p[u];~i;i=e[i].nxt){
        ll v=e[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!scc[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]){
        sccnum++;
        while(1){
            ll x=st[top--];
            num[sccnum]++;
            scc[x]=sccnum;
            if(x==u) break;
        }
    }
}

void init(){
    memset(p,-1,sizeof(p));edn=-1;
    memset(in,0,sizeof(in));
    memset(ou,0,sizeof(ou));
    memset(num,0,sizeof(num));
    memset(dfn,0,sizeof(dfn));
    memset(scc,0,sizeof(scc));
    sccnum=idx=top=0;
}

void rebuild(){
    for(int x=1;x<=n;x++){
        int u=scc[x];
        for(int i=p[x];~i;i=e[i].nxt){
            int v=scc[e[i].v];
            if(u==v) continue;
            ou[u]++,in[v]++;
        }
    }
}
int main(){
    scanf("%lld",&t);
    while(t--){
        init();
        scanf("%lld%lld",&n,&m);
        for(ll i=1;i<=m;i++){
            scanf("%lld%lld",&u,&v);
            add(u,v);
        }
        for(ll i=1;i<=n;i++)
            if(!scc[i]) tarjan(i);
        if(sccnum==1) printf("Case %lld: -1\n",++cs);
        else{
            rebuild();
            ll tmp=n;
            for(ll i=1;i<=sccnum;i++){
                if(in[i]==0||ou[i]==0){
                    tmp=min(tmp,num[i]);
                }
            }
            ll ans=n*n-n-tmp*(n-tmp);
            printf("Case %lld: %lld\n",++cs,ans-m);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值