多校联赛4的一道题,,给一个有向图,问最多加多少条边后仍然不是强联通,以前总会遇到问最少加几条边让图成一个强连通图,比赛时自己就找到了答案,当时想着添加最多的边后一定是将原来的图连成两个强连通分量,而两个强连通分量间的边最多是两个联通分量的点数之积,再加上每个联通分量内部的点的边数就是所有的边数,再减去原来的边就是新加上的边了,把图强连通缩点后,求出每个出度或入度为0的联通分量为一个联通分量,剩余的所有联通分量连成一个联通分量形成的边数,取最大的。当时敲完代码提交wrong了,就怀疑自己的算法,自己又证明了一下,如果n个点,分为两个联通分量,一个点数为a,一个为b,边数就是a*(a-1)+b*(b-1)+a*b-m=n*n-a*b-n,所以要保证a*b尽量小,所以要选点数最小的入度或出度为0的联通分量为一个,剩余的点为一个联通分量,,,,最后发现自己的代码有问题,平常总习惯点坐标从0开始,所以写成了i<n,,,,,
#include<stdio.h>
#include<string.h>
#include<stack>
#define N 100010
using namespace std;
int belong[N],ins[N],low[N],dfs[N],idx,ans,head[N],num,n,m,in[N],out[N],cot[N];
struct edge
{
int st,ed,next;
}E[N];
void addedge(int x,int y)
{
E[num].st=x;
E[num].ed=y;
E[num].next=head[x];
head[x]=num++;
}
stack<int>Q;
void Tarjan(int u)
{
int i,v;
dfs[u]=low[u]=idx++;
ins[u]=1;
Q.push(u);
for(i=head[u];i!=-1;i=E[i].next)
{
v=E[i].ed;
if(dfs[v]==-1)
{
Tarjan(v);
low[u]=low[u]>low[v]?low[v]:low[u];
}
else if(ins[v]==1)
low[u]=low[u]>dfs[v]?dfs[v]:low[u];
}
if(dfs[u]==low[u])
{
do
{
v=Q.top();
Q.pop();
ins[v]=0;
belong[v]=ans;
cot[ans]++;
}while(v!=u);
ans++;
}
}
int main()
{
int t,i,x,y,op=1,max;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
num=0;
for(i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
addedge(x,y);
}
memset(dfs,-1,sizeof(dfs));
memset(ins,0,sizeof(ins));
memset(cot,0,sizeof(cot));
idx=ans=0;
for(i=1;i<=n;i++)
if(dfs[i]==-1)
Tarjan(i);
printf("Case %d: ",op++);
if(ans==1)
{printf("-1\n");continue;}
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(i=0;i<num;i++)
{
x=belong[E[i].st];
y=belong[E[i].ed];
if(x==y)
continue;
out[x]++;
in[y]++;
}
max=9999999;
for(i=0;i<ans;i++)
{
if((in[i]==0||out[i]==0)&&max>cot[i])
max=cot[i];
}
printf("%d\n",n*n-max*(n-max)-n-m);
}
return 0;
}