1. 程式人生 > >【bzoj2744】[HEOI2012]朋友圈 二分圖匹配

【bzoj2744】[HEOI2012]朋友圈 二分圖匹配

算是一道好題了吧,竟然自己想出來了。

首先如果是一般圖的最大團,那麼肯定是不可做,所以這道題的條件一定有什麼性質。

仔細一看,對於A國,我們分成奇數和偶數兩類點,我們發現邊全都是在兩類點之間的,同類點之間沒有邊。

這不是一個二分圖嘛?二分圖的最大團?嘿嘿嘿,最大為2吧。

再看B國,同樣的做法,但是我們發現同類點之間兩兩有邊。

貌似有一個性質,二分圖的最大獨立集等於它補圖的最大團(一般圖適不適用呢?)。

我們發現B國的補圖恰好是一個二分圖,於是問題就解決了。

先列舉A國中選哪0、1、2個點,然後再挑出B國中同時與這幾個點相連的點,建出補圖,求最大獨立集=總點數-最大匹配數。

注意:

貌似直接清零並不優美,所以從Po姐那裡學習了一下時間戳,大概就是記錄一下每個陣列使用的時間吧。

#include<cstdio>  
    #include<cstring>  
    #include<cstdlib>  
    #include<cmath>  
    #include<iostream>  
    #include<algorithm>  
    #define maxn 3010  
    #define maxm 3010*3010  
       
    using namespace std;  
       
    int b[maxn][maxn],head[maxn],to[maxm],next[maxm];  
    int A[maxn],B[maxn];  
    int tag[maxn],lk[maxn],tim[maxn];  
    int vis[maxn];  
    int na,nb,m,num,ans,T,T1,T2;  
       
    void addedge(int x,int y)  
    {  
        num++;to[num]=y;next[num]=head[x];head[x]=num;  
    }  
       
    bool find(int x)  
    {  
        for (int p=head[x];p;p=next[p])  
          if (tag[to[p]]==T1 && vis[to[p]]!=T2)  
          {  
            vis[to[p]]=T2;  
            if (tim[to[p]]!=T2 || !lk[to[p]] || find(lk[to[p]]))  
            {  
                lk[to[p]]=x;  
                tim[to[p]]=T2;  
                return 1;  
            }  
          }  
        return 0;  
    }  
       
    int Count(int x)  
    {  
        int ans=0;  
        for (int i=x;i;i-=(i&(-i))) ans++;  
        return ans;  
    }  
       
    int solve()  
    {  
        int ans=0;  
        for (int i=1;i<=nb;i++)  
          if (tag[i]==T1)  
          {  
            T2++;  
            if (find(i)) ans++;  
          }  
          else ans++;  
        return nb-ans;  
    }  
       
    int main()  
    {  
        T1=T2=ans=0;  
        scanf("%d%d%d",&na,&nb,&m);  
        for (int i=1;i<=na;i++) scanf("%d",&A[i]);  
        for (int i=1;i<=nb;i++) scanf("%d",&B[i]);  
        for (int i=1;i<=m;i++)  
        {  
            int x,y;  
            scanf("%d%d",&x,&y);  
            b[x][y]=1;  
        }  
        for (int i=1;i<=nb;i++)  
          if (B[i]&1)  
            for (int j=1;j<=nb;j++)  
              if (!(B[j]&1))  
                if (!(Count(B[i]|B[j])&1)) addedge(i,j);  
        ans=max(ans,solve());  
        for (int i=1;i<=na;i++)  
        {  
            T1++;  
            for (int j=1;j<=nb;j++)  
              if (b[i][j]) tag[j]=T1;  
            ans=max(ans,solve()+1);  
        }  
        for (int i=1;i<=na;i++)  
          if (A[i]&1)  
            for (int j=i+1;j<=na;j++)  
              if (!(A[j]&1))  
              {  
                T1++;  
                for (int k=1;k<=nb;k++)  
                    if (b[i][k] && b[j][k]) tag[k]=T1;  
                ans=max(ans,solve()+2);  
              }  
        printf("%d\n",ans);  
        return 0;  
    }