链接:点击打开链接
题意:问一个有向图最多添加多少条边使得这个图仍不是强连通图,如果这个图已经是强连通图则直接输出-1
代码:
#include <vector>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int V,E;
vector<int> G[100005];
vector<int> rG[100005];
vector<int> vs;
bool used[100005];
int in[100005],out[100005];
int x[100005],y[100005],cmp[1000005],cnt[1000005];
void addedge(int from,int to){
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v){
int i;
used[v]=1;
for(i=0;i<G[v].size();i++)
if(!used[G[v][i]])
dfs(G[v][i]);
vs.push_back(v);
}
void rdfs(int v,int k){
int i;
used[v]=1;
cmp[v]=k;
for(i=0;i<rG[v].size();i++)
if(!used[rG[v][i]])
rdfs(rG[v][i],k);
}
int scc(){
int i,k;
memset(used,0,sizeof(used));
vs.clear();
for(i=0;i<V;i++)
if(!used[i])
dfs(i);
memset(used,0,sizeof(used));
k=0;
for(i=vs.size()-1;i>=0;i--)
if(!used[vs[i]])
rdfs(vs[i],k++);
return k;
} //强连通模板
int main(){ //因为要使得最后图不是强连通,并且
int t,i,j,ans,cas,tmp; //添加的边最多,因此最后图的强连通
scanf("%d",&t); //分量应为2,并且两部分都是完全图,
for(cas=1;cas<=t;cas++){ //并且两部分之间可连接单向边
scanf("%d%d",&V,&E);
for(i=0;i<V;i++){ //因为两部分都是完全图,因此问题变为
G[i].clear(); //V*(V-1)条边减去E后最少再减去多少条
rG[i].clear(); //也就是两个强连通分量可以连的单向边
vs.clear(); //的数量,有因为x+y和一定时,两个数
} //差距越大x*y越小,因此选择最小的x或y
for(i=0;i<E;i++){ //作为其中一个部分
scanf("%d%d",&x[i],&y[i]);
x[i]--,y[i]--;
addedge(x[i],y[i]);
}
ans=scc();
if(ans==1){ //如果是强连通分量直接输出-1
printf("Case %d: -1\n",cas);
continue;
}
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(cnt,0,sizeof(cnt));
for(i=0;i<V;i++) //数出每个强连通分量中的点的个数
cnt[cmp[i]]++;
for(i=0;i<E;i++){
if(cmp[x[i]]!=cmp[y[i]]){ //记录每个强连通分量的入度和出度
out[cmp[x[i]]]++;
in[cmp[y[i]]]++;
}
}
tmp=INF;
for(i=0;i<V;i++)
if(in[cmp[i]]==0||out[cmp[i]]==0)
tmp=min(tmp,cnt[cmp[i]]);
printf("Case %d: %d\n",cas,V*(V-1)-E-tmp*(V-tmp));
}
return 0;
}