【bzoj2744】[HEOI2012]朋友圈 二分圖匹配
阿新 • • 發佈:2019-02-03
算是一道好題了吧,竟然自己想出來了。
首先如果是一般圖的最大團,那麼肯定是不可做,所以這道題的條件一定有什麼性質。
仔細一看,對於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; }