官方题解:
首先二分图可以分成两类点X和Y, 完全二分图的边数就是∣X∣⋅∣Y∣.我们的目的是max{∣X∣⋅∣Y∣}, 并且∣X∣+∣Y∣=n.
把原图黑白染色, 每个联通块有ai个黑点, bi个白点, 于是就是要确定ai属于X还是属于Y. 然后我们考虑dp, dpi,x表示用了前i个联通块, ∣X∣=x是否可行. dp方程很容易确定, dpi,x=dpi−1,x−a[i] or dpi−1,x−b[i].
直接暴力是O(n2)的, 可以考虑用bitset优化, 这样就可以过了. 实际上由于数据很难造, 一些稍加优化的n2也可以过的。
不懂bitset或者不会用的童鞋请看:http://blog.csdn.net/piaocoder/article/details/47177891
#include <iostream>
#include <cstdio>
#include <bitset>
#include <algorithm>
#define N 110000
using namespace std;
bitset<11000>ans;
int s[N][2],fa[N],n,m,a[N];
int find_set(int x){
if(x == fa[x])
return x;
int t = fa[x];
fa[x] = find_set(fa[x]);
a[x] = a[x]^a[t];
return fa[x];
}
void solved(){
for(int i = 1; i <= n; i++)
ans[i] = 0;
ans[0] = 1;
for(int i = 1; i <= n; i++)
s[find_set(i)][a[i]]++;
for(int i=1;i<=n;i++)
if(fa[i]==i){
//cout<<s[i][0]<<" "<<s[i][1]<<endl;
ans = (ans<<s[i][0])|(ans<<s[i][1]);
}
int maxn=0;
for(int i=0;i<=n;i++)
if(ans[i])
maxn = max(maxn,i*(n-i)-m);
printf("%d\n",maxn);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
fa[i]=i,s[i][0]=0,s[i][1]=0,a[i]=0;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
int fx = find_set(x),fy = find_set(y);
if(fx == fy)
continue;
fa[fx] = fy;
a[fx] = 1^a[x]^a[y];
}
solved();
}
return 0;
}