首先二分图可以分成两类点X和Y, 完全二分图的边数就是|X| * |Y|,我们的目的是max {|X| *|Y|},并且|X| + |Y| = n,开口向下的抛物线,只要求出
X-Y的最小值
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mem0(a) memset(a,0,sizeof(a))
#define mem1(a) memset(a,-1,sizeof(a))
#define mem(a, b) memset(a, b, sizeof(a))
typedef long long ll;
const int maxn=100100;
int n,m;
vector<int>G[maxn];
int dp[maxn];
int vis[maxn];
int c[maxn];
int a,b;
int cnt[2];
void dfs(int u,int c){
vis[u]=1;
if(c==0)
a++;
else
b++;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(vis[v]==1)
continue;
dfs(v,c^1);
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
int x,y;
mem0(vis);
mem0(dp);
for(int i=1;i<=n;i++)
G[i].clear();
for(int i=0;i<m;i++){
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
int res=0,res2=0;
int mx=0;
mem0(c);
for(int i=1;i<=n;i++){ //a,b表示为将其分成两块
a=0;
b=0;
if(!vis[i]){
dfs(i,0);
if(a<b)
swap(a,b);
res2+=b;
mx=max(mx,abs(a-b));
c[abs(a-b)]++;
res+=abs(a-b);
}
}
int T=0;
dp[0]=1;
for(int i=1;i<=mx;i++){ //背包的优化
if(c[i])
while(c[i]--){
for(int j=T;j>=0;j--)
dp[j+i]=dp[j];
T+=i;
}
}
int res3=res/2; //从中间开始,是否被有dp为1
for(;res3<=res;res3++){
if(dp[res3])
break;
}
printf("%d\n",(res3+res2)*(res2+res-res3)-m);
}
return 0;
}