链接:https://cn.vjudge.net/problem/HDU-4635
题意:给你一个有向简单图,问最多加多少边,加完之后该图仍不是强连通分量。如果已经是则输出-1。
思路:肯定是加到再加一条边就变成强连通分量为止。现在考虑怎么使加的边尽可能的多,也就是加完边后,边的总数尽可能的大。那么,最终的图肯定是在两个完全图之间,再加一些边组成。假设现在两个完全图的点数分别为x,y。可以得到:x+y=n;最后加的边肯定是一方(A)的所有点指向另一方(B)的所有点,而另一方(B)的所有点,没有一条边指向A的点。(也就是有一方出度为0或入度为0。)最后的边数也就是x*(x-1)+y*(y-1)+x*y,将x+y=n代入得,n*n-n-x*y。那么,加的边数就是n*n-n-x*y-m,要使此式的结果尽可能的大,那就是要让x*y尽可能的小。显然,x和y的差距越大,x*y的值越小,那么问题转化为,求含有最少点的强连通分量。注意,这个强连通分量要么出度为0,要么入度为0。可能有些小伙伴会问,那会不会所有的强连通分量的出入度都不为0,当然是不会的。如果所有的强连通分量的出入度都不为0,那么,他们合起来也是一个强连通分量啊。这种情况,已经被我们判断-1的时候排除掉了。详情看代码。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 1e5+10;
struct node
{
int to,nxt;
}g[M];
int head[N],cnt;
int num[N],dfn[N],low[N],id,sta[N],top,color[N],cl;
int in[N],out[N];
bool vis[N];
ll n,m,ans;
void Init()
{
cnt=top=id=cl=0;
for(int i=1;i<=n;i++)
head[i]=-1,in[i]=out[i]=dfn[i]=vis[i]=0;
}
void add(int u,int v)
{
g[cnt].to=v; g[cnt].nxt=head[u]; head[u]=cnt++;
}
void tarjan(int u)
{
int v;
dfn[u]=low[u]=++id; sta[++top]=u; vis[u]=1;
for(int i=head[u];i!=-1;i=g[i].nxt)
{
v=g[i].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
color[u]=++cl;
num[cl]=1;
while(sta[top]!=u)
{
color[sta[top]]=cl; vis[sta[top--]]=0;
num[cl]++;
}
vis[sta[top--]]=0;
}
}
int main()
{
int t,u,v,tt=0;
ll temp;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
Init();
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
printf("Case %d: ",++tt);
if(cl==1)
{
printf("-1\n");
continue;
}
for(u=1;u<=n;u++)
for(int i=head[u];i!=-1;i=g[i].nxt)
{
v=g[i].to;
if(color[u]==color[v]) continue;
out[color[u]]++,in[color[v]]++;
}
temp=1LL*n*n-n-m;
ans=0;
for(int i=1;i<=cl;i++)
if(!in[i]||!out[i]) ans=max(ans,temp-1LL*(n-num[i])*num[i]);
printf("%lld\n",ans);
}
return 0;
}