题目传送门
1 /*
2 二分图点染色:这题就是将点分成两个集合就可以了,点染色用dfs做, 剩下的点放到点少的集合里去
3 官方解答:首先二分图可以分成两类点X和Y, 完全二分图的边数就是|X|*|Y|.我们的目的是max{|X|*|Y|}, 并且|X|+|Y|=n.
4 修正:实现多连通块染色,然后贪心选择,将两个集合个数差大的连通块优先添加,能尽量使得un*vn最大
5 */
6 #include <cstdio>
7 #include <algorithm>
8 #include <cstring>
9 #include <vector>
10 #include <map>
11 using namespace std;
12
13 const int MAXN = 1e4 + 10;
14 const int MAXM = 1e5 + 10;
15 const int INF = 0x3f3f3f3f;
16 vector<int> G[MAXN];
17 int col[2*MAXN];
18 bool vis[MAXN];
19 struct Block { //连通块
20 int u, v;
21 bool operator < (const Block &r) const {
22 return u - v > r.u - r.v;
23 }
24 }b[MAXN];
25 int n, m, un, vn;
26
27 void DFS(int u, int c) {
28 col[c]++; vis[u] = true;
29 for (int i=0; i<G[u].size (); ++i) {
30 int v = G[u][i];
31 if (vis[v]) continue;
32 DFS (v, c ^ 1);
33 }
34 }
35
36 int main(void) { //BestCoder 1st Anniversary($) 1004 Bipartite Graph
37 //freopen ("D.in", "r", stdin);
38
39 int T; scanf ("%d", &T);
40 while (T--) {
41 scanf ("%d%d", &n, &m);
42 for (int i=1; i<=n; ++i) G[i].clear ();
43 for (int i=1; i<=m; ++i) {
44 int u, v; scanf ("%d%d", &u, &v);
45 G[u].push_back (v); G[v].push_back (u);
46 }
47
48 int color = 0;
49 memset (vis, false, sizeof (vis));
50 memset (col, 0, sizeof (col));
51 for (int i=1; i<=n; ++i) {
52 if (vis[i]) continue;
53 DFS (i, color); color += 2;
54 }
55 int cnt = 0;
56 for (int i=0; i<color; i+=2) {
57 b[++cnt].u = col[i]; b[cnt].v = col[i^1];
58 if (b[cnt].u < b[cnt].v) swap (b[cnt].u, b[cnt].v);
59 }
60 sort (b+1, b+1+cnt);
61
62 un = vn = 0;
63 for (int i=1; i<=cnt; ++i) {
64 if (un <= vn) {
65 un += b[i].u; vn += b[i].v;
66 }
67 else {
68 un += b[i].v; vn += b[i].u;
69 }
70 }
71 printf ("%d
", un * vn - m);
72 }
73
74 return 0;
75 }