https://vjudge.net/problem/HDU-4635#author=634579757
有t组数据,给了一个n个点m条边的有向图,问给此图最多加多少条边,满足如下条件:
1:此图不能强连通
2:没有重边和自环
3:当图本身就是强连通的时候就输出-1
那么现在我们考虑,如何加边使得加的边尽可能的多,又不强连通. 在使图不强连通的前提下,要添加尽可能多的边。边至多有n∗(n−1)条,而已经给了m条,那么所能添加的边数不可能超过k=n∗(n−1)−m。
这k条边还有部分不能添加,一添加立刻就强连通。一个强连通图最少只需要n条边,根据强连通的特性,缩点之后必定是不会有环的存在的,那么只要继续保持没有环的存在即可。我们只要让其中1个强连通分量没有出度,或者没有入度就行了。到底选哪个强连通分量?选点数少的,这点在后面会了解。
例如1×10=10,2×9=18,故选点数越小的SCC越好
如果挑出1个出度(入度也行的,同理)为0的强连通分量,该强连通分量的点数为d,那么答案就是k-d(n-d),即不允许有其他n-d个点有任何一条边指向此强连通分量,其他边都是可以存在的。那么要使得k-d(n-d)最大,那么d(n-d)要最小,就是上面讲的要选哪个强连通分量的原因。
那么应该找到缩点后出/入度为0的且包含点数最少的强连通分量来计算即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int N=1e5+5;
int ver[N],Next[N],head[N],dfn[N],low[N];
int stack[N],ins[N],in[N],out[N],scc_cnt[N],c[N];
int n,m,tot,num,top,cnt,t,count=1,minv;
void add(int x,int y){
ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++num;
stack[++top]=x,ins[x]=1;
for(int i=head[x];i;i=Next[i])
if(!dfn[ver[i]]){
tarjan(ver[i]);
low[x]=min(low[x],low[ver[i]]);
}
else if(ins[ver[i]])
low[x]=min(low[x],dfn[ver[i]]);
if(dfn[x]==low[x]){
cnt++;int y;
do{
y=stack[top--],ins[y]=0;
c[y]=cnt,scc_cnt[cnt]++;
}while(x!=y);
}
}
void init(){
minv=1e9;cnt=0;top=0;tot=0;
memset(head,0,sizeof head);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(scc_cnt,0,sizeof scc_cnt);
memset(in,0,sizeof in);
memset(out,0,sizeof out);
memset(c,0,sizeof c);
}
int main(){
scanf("%d",&t);
while(t--){
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int x=1;x<=n;x++)
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(c[x]==c[y]) continue;
in[c[y]]++,out[c[x]]++;
}
for(int i=1;i<=cnt;i++)
if(!in[i]||!out[i]) minv=min(minv,scc_cnt[i]);
if(cnt==1) printf("Case %d: -1\n",count++);
else printf("Case %d: %lld\n",count++,(ll)n*(n-1)-(ll)m-(ll)minv*(n-minv));
}
return 0;
}
京公网安备 11010502036488号