题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4635>
题意:
在一张有向图上加尽可能多的边,使它不成为强连通图。输出能加的边的数量。
题解:
首先Tarjan缩点。考虑把图上的点分成两块,左边点的数量为,右边点的数量为。,为点的总数。
左边所有的点互相连接,一共有条边;右边所有的点互相连接,一共有条边。
左边的点与右边连一条有向边,一共有条边。
所以总共能够构成条边。
拆开化简就是。要使这个值最大,就要是尽可能地小。因为,所以最小的情况就是两者差值最大的情况。
所以我们可以找出所有入度为零或出度为零的点,找一个最小值代入为。将最后构造出来的边数减去原来存在的边即是答案。
#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);
}
}
}