[WC2016]挑戰NPC(一般圖最大匹配)
阿新 • • 發佈:2020-07-14
[WC2016]挑戰NPC(一般圖最大匹配)
題解時間
思路十分有趣。
考慮一個筐只有不多於一個球才有1的貢獻代表什麼。
很明顯等效於有至少兩個位置沒有被匹配時有1的貢獻。
進而可以構造如下模型:
每個筐拆成三個點,三個點之間相互連邊。
對於球可以匹配某個筐,將球向筐的三個點都連邊。
這樣一來,如果有一個筐只有不多於一個點被匹配,那麼剩下的兩個點可以自己匹配增加答案。
如此最終結果是 $ ans-n $ 。
需要用到一般圖最大匹配也即帶花樹。
由於答案要求輸出匹配方案,所以要注意先匹配球再匹配筐。
#include<bits/stdc++.h> using namespace std; typedef long long lint; struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}}; template<typename TP>inline void read(TP &tar) { TP ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();} tar=ret*f; } namespace RKK { const int N=1011,M=300011; struct sumireko{int to,ne;}e[M];int he[N],ecnt; void addline(int f,int t){e[++ecnt].to=t;e[ecnt].ne=he[f],he[f]=ecnt;} int n,m,o,tar[N]; int f[N];int find(int x){return f[x]==f[f[x]]?f[x]:f[x]=find(f[x]);} int dep[N],da; int pre[N],col[N]; queue<int>q; int lca(int x,int y) { da++;x=find(x),y=find(y); while(dep[x]!=da){dep[x]=da,x=find(pre[tar[x]]);if(y) swap(x,y);} return x; } void uno(int x,int y,int z) { while(find(x)!=z) { pre[x]=y,y=tar[x]; if(col[y]==2) col[y]=1,q.push(y); if(find(x)==x) f[x]=z;if(find(y)==y) f[y]=z; x=pre[y]; } } int hun(int sp) { memset(col,0,sizeof(col)),memset(pre,0,sizeof(pre)); for(int i=1;i<=n+m*3;i++) f[i]=i;while(!q.empty()) q.pop(); q.push(sp),col[sp]=1; while(!q.empty()) { int x=q.front();q.pop(); for(int i=he[x],t=e[i].to;i;i=e[i].ne,t=e[i].to)if(col[t]!=2&&find(x)!=find(t)) { if(!col[t]) { col[t]=2,pre[t]=x; if(!tar[t]) { int lst=0;while(t) lst=tar[pre[t]],tar[t]=pre[t],tar[pre[t]]=t,t=lst; return 1; }else col[tar[t]]=1,q.push(tar[t]); }else{int z=lca(x,t);uno(x,t,z),uno(t,x,z);} } } return 0; } int main() { int TAT;read(TAT);while(TAT--) { read(n),read(m),read(o); for(int i=1;i<=m;i++) addline(i,m+i),addline(m+i,i),addline(m+i,m*2+i),addline(m*2+i,m+i),addline(m*2+i,i),addline(i,m*2+i); for(int i=1,x,y;i<=o;i++) read(x),read(y), addline(m*3+x,y),addline(y,m*3+x), addline(m*3+x,m+y),addline(m+y,m*3+x), addline(m*3+x,m*2+y),addline(m*2+y,m*3+x); int ans=0; for(int i=m*3+1;i<=m*3+n;i++)if(!tar[i]) ans+=hun(i); for(int i=1;i<=m*3;i++)if(!tar[i]) ans+=hun(i); printf("%d\n",ans-n); for(int i=1;i<=n;i++) printf("%d ",(tar[m*3+i]-1)%m+1);putchar('\n'); memset(he,0,sizeof(he)),ecnt=0,memset(dep,0,sizeof(dep)),da=0,memset(tar,0,sizeof(tar)); } return 0; } } int main(){return RKK::main();}