BZOJ2744 HEOI2012朋友圈(二分图匹配)

  先考虑B国。容易发现a xor b mod 2=0即二进制末位相同,那么可以据此将所有人分成两部分,每一部分各自是一个完全图。然后再将a or b有奇数个1的边连上,现在需要求的就是这样一个图里的最大团。我们知道最大团=反图最大独立集,这个图的反图显然是一个二分图,那么跑二分图匹配就可以求出这个了。

  A国同样根据二进制末位分成两部分。显然不可能选择末位相同的两人。于是暴力枚举在A国选择哪些人,只留下B国与其有边的人跑匹配就可以了。

  至于复杂度……硬要卡的话这当然是个假题。

  在一些非常弱智的地方wa了好长时间,没救。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 3010
int n,m,k,a[N],b[N],p[N],q[N],tag[N],match[N],t,ans,tot,cnt;
bool flag[N][N],f[N][N];
struct data{int to,nxt;
}edge[N*N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
bool hungary(int k,int from)
{
    for (int i=p[k];i;i=edge[i].nxt)
    if (tag[edge[i].to]!=from)
    {
        tag[edge[i].to]=from;
        if (!match[edge[i].to]||hungary(match[edge[i].to],from))
        {
            match[edge[i].to]=k;
            return 1;
        }
    }
    return 0;
}
void pre()
{
    memset(p,0,m+1<<2);
    memset(match,0,m+1<<2);
    memset(tag,0,m+1<<2);
    tot=0;t=0;cnt=0;
}
void solve()
{
    for (int j=1;j<=cnt;j++)
        for (int k=1;k<=cnt;k++)
        if (f[q[j]][q[k]]) addedge(q[j],q[k]);
    for (int j=1;j<=cnt;j++)
    if (b[q[j]]&1) tot+=hungary(q[j],q[j]);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2744.in","r",stdin);
    freopen("bzoj2744.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read(),m=read(),k=read();
    for (int i=1;i<=n;i++) a[i]=read();
    for (int i=1;i<=m;i++) b[i]=read();
    for (int i=1;i<=k;i++)
    {
        int x=read(),y=read();
        flag[x][y]=1;
    }
    for (int i=1;i<=m;i++)
        for (int j=1;j<=m;j++)
        if ((b[i]&1)&&!(b[j]&1))
        {
            int x=b[i]|b[j],y=0;
            while (x) y^=1,x-=x&-x;
            if (!y) f[i][j]=1,addedge(i,j);
        }
    for (int i=1;i<=m;i++)
    if (b[i]&1) tot+=hungary(i,i);
    ans=m-tot;
    for (int i=1;i<=n;i++)
    {
        pre();
        for (int j=1;j<=m;j++)
        if (flag[i][j]) q[++cnt]=j;
        solve();
        ans=max(ans,cnt-tot+1);
    }
    for (int x=1;x<n;x++)
        for (int y=x+1;y<=n;y++)
        if ((a[x]^a[y])&1)
        {
            pre();
            for (int j=1;j<=m;j++)
            if (flag[x][j]&&flag[y][j]) q[++cnt]=j;
            solve();
            ans=max(ans,cnt-tot+2);
        }
    cout<<ans;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9609322.html
今日推荐