首先考慮如何最小化團內邊數。
考慮到如果兩個團大小分別為 \(x\le y\),那麼有 \((\binom{x - 1}{2} + \binom{y + 1}{2}) - (\binom{x}{2} + \binom{y}{2}) = -x + 1 + y = y - x + 1 \ge 1\)。
所以可以知道,當 \(|x - y|\) 越小時,團內邊數也最小。
接下來考慮如何求解團。
考慮到團的求解在 \(n\le 700\) 的情況下看起來並不是很好做,於是考慮轉化一下,不去求團。
考慮到題目中兩個團的條件。
一個啟發是想到二分圖的左部點右部點。
然後會發現左部點右部點內部需要滿足都沒有連邊,而團是要求內部都要有連邊。
於是能夠發現在補圖上的一個合法二分圖就對應著原圖上的兩個團。
那麼在補圖上跑出來的會有很多個二分圖。
現在相當於是要把這些二分圖組合起來,其中左部點和右部點是可以交換的。
那麼直接揹包就行了。
這裡的揹包可以用 bitset
最佳化,但是複雜度瓶頸不在這裡。
時間複雜度 \(\mathcal{O}(n^2)\)。
#include<bits/stdc++.h>
inline int Cn2(int n) {return n * (n - 1) / 2;}
const int maxn = 7e2 + 10;
int n;
int G[maxn][maxn];
std::bitset<maxn * 2> f;
int col[maxn], vis[maxn];
bool dfs(int u, int &sum) {
if (vis[u]++) return false;
sum += col[u] ? 1 : -1;
for (int v = 1; v <= n; v++)
if (G[u][v]) {
if (col[v] == -1) {
col[v] = ! col[u];
if (dfs(v, sum))
return true;
}
if (col[u] == col[v])
return true;
}
return false;
}
int main() {
int m; scanf("%d%d", &n, &m);
for (int x, y; m--; ) {
scanf("%d%d", &x, &y);
G[x][y] = G[y][x] = 1;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
G[i][j] ^= i != j;
memset(col, -1, sizeof(int) * (n + 1));
f[maxn] = 1;
for (int i = 1; i <= n; i++)
if (col[i] == -1) {
col[i] = 0;
int sum = 0;
if (dfs(i, sum))
return puts("-1"), 0;
sum = abs(sum);
f = (f << sum) | (f >> sum);
}
for (int i = 0; i <= n; i++)
if (f[maxn + i])
return printf("%d\n", Cn2((n + i) / 2) + Cn2((n - i) / 2)), 0;
return 0;
}