不难得到如下结论:如果原图不是强连通, 那么最优解肯定是只剩两个强连通分量且每个分量内都为完全图, 可以列出数学表达式不难
发现最后的结果只和两个分量中的点数的乘积有关, 由于两者的和一定使两者的差尽可能大, 但一个点如果既有出度又有入度就不能单
独将它作为最后的一个点, 所以我要考虑只有出度或者入度的点。
#include <iostream>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <cmath>
using namespace std;
typedef long long LL;
const int N = 100005;
const int M = N << 4;
struct SCC {
struct Edge {
int v;
Edge* next;
void init(int a, Edge* e) {
v = a, next = e;
}
};
Edge E[M], * head[N];
int low[N], pre[N], no[N];
int cnt[N];
int n, tot, sum, tdfn, m;
int in[N], out[N];
stack<int> stk;
void init(int n, int m) {
this->n = n;
this->m = m;
for (int i = 0; i < n; i++) {
head[i] = 0;
no[i] = -1;
pre[i] = 0;
cnt[i] = 0;
}
tot = 0, sum = 0, tdfn = 0;
}
void add(int u, int v) {
E[tot].init(v, head[u]);
head[u] = &E[tot++];
}
void dfs(int u) {
pre[u] = low[u] = ++tdfn;
stk.push(u);
for (Edge* e = head[u]; e; e = e->next) {
int v = e->v;
if (!pre[v]) {
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (no[v] == -1)
low[u] = min(low[u], pre[v]);
}
if (low[u] == pre[u]) {
sum++;
while(1) {
int x = stk.top();
stk.pop();
no[x] = sum - 1;
if (x == u) break;
}
}
}
LL run() {
for (int i = 0; i < n; i++)
if (pre[i] == 0)
dfs(i);
if (sum == 1) return -1;
int key = N;
for (int i = 0; i < n; i++) {
cnt[no[i]]++;
}
for (int i = 0; i < sum; i++)
in[i] = 0, out[i] = 0;
for (int i = 0; i < n; i++)
for (Edge* e = head[i]; e; e = e->next) {
int v = e->v;
if (no[i] == no[v]) continue;
in[no[v]]++;
out[no[i]]++;
}
for (int i = 0; i < sum; i++) {
if (in[i] && out[i]) continue;
key = min(key, cnt[i]);
}
LL res = (LL) n * n - n - m - (LL)key * (n - key);
return res;
}
}G;
int main() {
int test, u, v, n, m;
scanf("%d", &test);
int cas = 1;
while (test--) {
scanf("%d%d", &n, &m);
G.init(n, m);
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
u--, v--;
G.add(u, v);
}
cout << "Case " << cas++ << ": " << G.run() << endl;
}
return 0;
}