题意:给出一个有向图,问最多能添加多少条边,使得原图不是边双连通图,如果原本已经是边双连通图,输出-1
首先对于找最多的边,可以逆向考虑一下,先把图补满(即任意两点之间都有一条边)删除最少的边,对于原本已经有的边肯定是不能删的,首先在满的基础上减去原有的m条边,并用这m条边缩点,此时块与块之间的关系有了。然后只需要找出个点数最少的块,删去他与剩下所有块里的点之间的一条边即可,此时只能由点数少的块到点数大的块或点数大的块到点数小的块。因为x+y是个固定值时,x和y的差距越大,x*y越小。故此时删去边最少。
会有一个问题,这样删的时候会不会把原来的m条边删去?
其实是不会的,因为删边所用的图是由m条边缩点后的图,这m条边的关系只可能是每个块的内部与块与块之间,我们删去的是 点数最少块里的点与 其他所有块的点之间的一条边,每个块的点与另一个块之间依然相连至少一条边,而原本m条边,每个块的点与另一个块的点之间最多也只有一条边(两条边就连通了,缩点会直接缩到一个块里)
#include
#include
#include
#include
#include
#include
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=200005;
int low[N],dfn[N],dfs_num,vis[N];//tarjan
int tot,first[N];//邻接表
int col_num,top,block[N];//染色
int st[N];//染色与点双联通
int id[N],od[N],num[N];
struct edge
{
int v,next;
} e[N*20];
void init(int n)
{
mem(first,-1);
mem(dfn,0);
mem(low,0);
mem(vis,0);
mem(id,0);
mem(od,0);
mem(num,0);
mem(st,0);
tot=0,col_num=0,dfs_num=0,top=0;
}
void add(int u,int v)
{
e[tot].v=v;
e[tot].next=first[u];
first[u]=tot++;
}
void dyeing(int u)//染色
{
int v;
if(low[u]==dfn[u])
{
++col_num;
do
{
v=st[--top];
block[v]=col_num;
num[col_num]++;
vis[v]=0;
}
while(v!=u);
}
}
void dfs(int u,int fa)
{
low[u]=dfn[u]=++dfs_num;
vis[u]=1;
st[top++]=u;
for(int i=first[u]; ~i; i=e[i].next)
{
int v=e[i].v;
if(!dfn[v])
{
dfs(v,u);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],dfn[v]);
}
dyeing(u);//染色
}
void solve(int n,int m)
{
for(int i=1; i<=n; i++)
{
if(!dfn[i])dfs(i,-1);
}
if(col_num==1)
{
puts("-1");
return;
}
for(int i=1; i<=n; i++)
{
for(int j=first[i]; ~j; j=e[j].next)
{
int v=e[j].v;
if(block[i]==block[v])continue;
od[block[i]]++;
id[block[v]]++;
}
}
ll ans=n*(n-1)-m;
int minn=inf;
for(int i=1;i<=col_num;i++)
{
if(!id[i]||!od[i])
minn=min(minn,num[i]);
}
ans-=minn*(n-minn);
printf("%lld\n",ans);
}
int main()
{
int t,n,m,x,y,q=0;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init(n);
for(int i=1; i<=m; i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
printf("Case %d: ",++q);
solve(n,m);
}
return 0;
}