题意:
给出一个完全二分图中的一些边,求这个完全二分图中最多还有多少条边;
点数<=10000;
题解:
BC的pre test太坑爹了!
显然如果想让边数最多,两边的点数越均衡越好;
考虑到给出的图可能是不连通的,我用了并查集来维护这些点集;
(和图上染色的算法就差了一个反ackerman函数但是省了存边深搜的麻烦)
这样处理之后,就得到了一些二分图,之后就是将这些二分图组合起来,得到一个边数最大的二分图;
可以用bool背包处理,但是最坏复杂度是O(n^2)的,这里用bitset来优化dp;
可以把bitset理解为一个bool数组,并且是一个支持数组间位运算的数组;
这些运算的复杂度是O(n/32),dp的复杂度也就降到了O(n^2/32) (实际上似乎更快一些?);
dp的具体处理见代码吧,总体来说还是十分好用的;
加权的并查集有点绕。。写挂了一次;
代码:
#include<bitset>
#include<stack>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 11000
using namespace std;
int f[N];
bool val[N];
int hdu[N][2];
bitset<N>bs,t;
int find(int x)
{
if(f[x]==x)
return x;
else
{
int t=find(f[x]);
val[x]=val[x]^val[f[x]];
f[x]=t;
return t;
}
}
int main()
{
int c,T,n,m,i,j,k,x,y,fx,fy,cnt;
scanf("%d",&T);
for(c=1;c<=T;c++)
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
f[i]=i,val[i]=0;
memset(hdu,0,sizeof(hdu));
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
fx=find(x),fy=find(y);
if(fx!=fy)
f[fy]=x,val[fy]=1;
}
for(i=1;i<=n;i++)
{
fx=find(i);
hdu[fx][val[i]]++;
}
for(i=1,cnt=0;i<=n;i++)
{
if(hdu[i][0]||hdu[i][1])
{
cnt++;
swap(hdu[i][0],hdu[cnt][0]);
swap(hdu[i][1],hdu[cnt][1]);
}
}
bs=0;
bs[0]=1;
for(i=1;i<=cnt;i++)
{
t=bs;
bs|=t<<hdu[i][0];
bs|=t<<hdu[i][1];
if(bs[n/2])
break;
}
for(i=0;i<=n/2;i++)
{
if(bs[n/2+i])
{
i=n/2+i;
break;
}
if(bs[n/2-i])
{
i=n/2-i;
break;
}
}
printf("%d\n",(n-i)*i-m);
}
return 0;
}