题意:对于一个有向图,最多能加几条边依然使得图不变为强联通,如果原来就是强联通,输出-1,否则,输出最多能增加的边数
思路:最优解就是一个完全图删边的过程,删到有且只有一个点入度为0或者初度为0,所能增加的边s=n*(n-1)-(n-1) ,若图里存在强联通分量,那么就必须先缩点,把一个强联通分量看成一个点,重新构图,找到一点符合只有入度为0或者初度为0的“点”,这些点内连成完全图1,把剩下的不属于这个强联通分量的其他点组成另一部图也连成完全图2,然后把这两部图连起来(有向边),s=x*(x-1)+y*(y-1)+xy (x、y)分别为图1图2的点的个数
代码;
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
#include <stack>
using namespace std;
const int M=100005;
struct node
{
int u,v;
int next;
} edge[M];
int indu[M],outdu[M],head[M];
int belong[M],dfn[M],low[M],top,vis[M],num;
int n,m,cnt,st;
int Stack[M];
bool instack[M];
int benum[M];
void addEgde(int u,int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++num;
Stack[++top]=u;
instack[u]=true;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
int v=edge[i].v;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])
{
int x,sum=0;
st++;
do
{
x=Stack[top--];
belong[x]=st;
instack[x]=false;
sum++;
}
while(x!=u);
benum[st]=sum;//记录同一个强联通的点的个数
}
}
int main()
{
int t,u,v,g=1;
scanf("%d",&t);
while(t--)
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
memset(benum,0,sizeof(benum));
memset(indu,0,sizeof(indu));
memset(outdu,0,sizeof(outdu));
st=top=num=cnt=0;
scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
{
scanf("%d%d",&u,&v);
addEgde(u,v);
}
for(int i=1; i<=n; i++)
if(!dfn[i])
tarjan(i);
if(st==1)
{
printf("Case %d: -1\n",g++);
continue;
}
for(int i=1; i<=n; i++)
{
for(int j=head[i]; j!=-1; j=edge[j].next)
{
if(belong[i]==belong[edge[j].v])continue;
outdu[belong[i]]++;
indu[belong[edge[j].v]]++;
}
}
long long ans=0;
for(int i=1; i<=st; i++)//连通分量
{
if(outdu[i]==0 || indu[i]==0)
{
int x=benum[i],y=n-x;
ans=max(ans,1LL*x*(x-1)+1LL*y*(y-1)+1LL*x*y-m);
}
}
printf("Case %d: %I64d\n",g++,ans);
}
return 0;
}