连边的最后肯定是两个集合x,y
x集合的每个元素,到y集合中的每个元素都是单向的边
x集合,和y集合都是完全图
设a为x集合的点的个数, b为y集合的
那么答案就是 a * b + a*(a-1) + b*(b-1) - m
n*n-a*b-n-m , 所以a*b尽量小, 即a和b的差值尽量大

缩点之后的点入度为0,或者出度为0才能成为x集合,y集合

 #pragma warning(disable:4996)
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <math.h>
#include <algorithm>
#include <map>
#include <set>
#include <functional>
using namespace std;
typedef __int64 LL;
const int N = + ;
int dfn[N], low[N], sccno[N], cnt, dfs_clock, sum[N], in[N], out[N];
vector<int> g[N];
stack<int> st;
/* */
void init(int n)
{
cnt = dfs_clock = ;
for (int i = ;i <= n;++i)
{ in[i] = out[i] = dfn[i] = low[i] = sccno[i] = sum[i] = ;
g[i].clear();
}
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++dfs_clock;
st.push(u);
for (int i = ; i<g[u].size(); ++i)
{
int v = g[u][i];
if (dfn[v] == )
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if (sccno[v] == )//因为有向图存在横插边,不能用横插边来更新low[u]
{
low[u] = min(low[u], low[v]);
}
}
//同样,因为强连通分量可以分布在根结点的两个分支上,所以在递归返回的时候调用
if (low[u] == dfn[u])
{
cnt++;
for (;;)
{
int x = st.top();
st.pop();
sccno[x] = cnt;
sum[cnt]++;
if (x == u)
break;
}
}
} int main()
{
int t, n, m;
int u, v;
scanf("%d", &t);
for (int k = ;k <= t;++k)
{
scanf("%d%d", &n, &m);
init(n);
for (int i = ;i <= m;++i)
{
scanf("%d%d", &u, &v);
g[u].push_back(v); }
for (int i = ;i <= n;++i)
if (dfn[i] == )
tarjan(i, -);
for (int u = ;u <= n;++u)
{
for (int i = ;i < g[u].size(); ++i)
{
int v = g[u][i];
if (sccno[u] != sccno[v])
{
in[sccno[v]]++;
out[sccno[u]]++;
}
}
}
if (cnt == )
{
printf("Case %d: -1\n", k);
continue;
}
LL ans = ;
for (int i = ;i <= cnt;++i)
{
if (!in[i] || !out[i])//出度为0或者入度为0的连通分量才能成为一个集合,剩余的成为另一个集合
{
LL a = sum[i];
LL b = n - a;
ans = max(ans, n*n - a*b - n - m);
}
}
printf("Case %d: %I64d\n", k, ans);
}
return ;
}
05-02 23:20