题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=4635
题意
给出一张无向简单图,问保持其简单图性质且不生成强连通图的前提下,可以加多少边。若已经强连通,答案为-1.
思路
易想到一加边策略,不断加边直到最终只划分为两个点集,两点集内强连通,两点集间只有单向边。设其较小的点集大小为x,易知此时边数为n*(n-1)-(n-x)*x。将这个数减去本来边的数量即为答案。
显然(均值不等式)我们应令x最小化。我们求SCC并缩点,新图中所有入度为0或者出度为0的点可以作为大小为x的点集。挑选其最小的更新答案即可。
教训/收获
没注意到入度为0或出度为0才能满足条件。还是太菜,继续修炼
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
#define int long long
using namespace std;
typedef long long ll;
const int maxn=200505;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,k;
int dfn[maxn],low[maxn];
bool in_sta[maxn];
stack<int>sta;
int S_id[maxn];
int scc_size[maxn];
int scc_cnt;
int head[maxn],cnt;
int dfn_cnt;
int in[maxn],out[maxn];
struct Edge{
int from,to,val,next;
}edge[maxn];
void init(){
memset(head,-1,sizeof head);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(in_sta,0,sizeof in_sta);
memset(scc_size,0,sizeof scc_size);
memset(in,0,sizeof in);
memset(out,0,sizeof out);
cnt=0;scc_cnt=0;dfn_cnt=0;
while(sta.size()) sta.pop();
}
void add(int u,int v,int w){
edge[cnt]={u,v,w,head[u]};
head[u]=cnt++;
}
void tarjan(int x){
dfn[x]=low[x]=++dfn_cnt;
sta.push(x);in_sta[x]=1;
for(int i=head[x];~i;i=edge[i].next){
int y=edge[i].to;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(in_sta[y]){
low[x]=min(low[x],dfn[y]);
}
}
if(dfn[x]==low[x]){
scc_cnt++;
int z;
do{
z=sta.top();sta.pop();
in_sta[z]=0;
S_id[z]=scc_cnt;
scc_size[scc_cnt]++;
}while(z!=x);
}
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
init();
int tn=1;
cin>>tn;
for(int ttn=1;ttn<=tn;ttn++){
cin>>n>>m;
init();
cout<<"Case "<<ttn<<": ";
k=m;
while(k--){
int u,v;
cin>>u>>v;
add(u,v,0);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
if(scc_cnt==1){
cout<<-1<<endl;
continue;
}
for(int i=0;i<cnt;i++){
int uid=S_id[edge[i].from];
int vid=S_id[edge[i].to];
if(uid==vid) continue;
in[vid]++;
out[uid]++;
}
int mi=inf;
for(int i=1;i<=scc_cnt;i++){
if(!in[i]||!out[i]){
mi=min(mi,(n-scc_size[i])*scc_size[i]);
}
}
int ans=n*(n-1)-mi-m;
cout<<ans<<endl;
}
}
|