20200501 zr T3【二分图and一般图匹配 Dinic+带花树 / 随机匈牙利 / 暴力切换】

题目描述:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题目分析:

在这里插入图片描述
我辛辛苦苦打了一发带花树+Dinic:

#include<bits/stdc++.h>
#define maxn 1<<17
#define maxm 15000005
using namespace std;
int n,N,k,m,bit[maxn],id[maxn];
int mat[maxn];
vector<int>A,B;
namespace Blossom{
	int F[maxn],typ[maxn],pre[maxn],q[maxn],L,R,vis[maxn],tim;
	int fir[maxn],nxt[maxm],to[maxm],tot;
	inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
	int find(int x){return !F[x]?x:F[x]=find(F[x]);}
	int LCA(int u,int v){
		for(++tim;;swap(u,v)) if(u){
			if(vis[u=find(u)]==tim) return u;
			vis[u]=tim,u=pre[mat[u]];
		}
	}
	void blossom(int u,int v,int tp){
		while(find(u)!=tp){
			pre[u]=v,v=mat[u];
			if(typ[v]==2) typ[q[++R]=v]=1;
			if(find(u)==u) F[u]=tp;
			if(find(v)==v) F[v]=tp;
			u=pre[v];
		}
	}
	void aug(int s){
		for(int i:A) F[i]=typ[i]=pre[i]=0;
		typ[q[L=R=1]=s]=1;
		while(L<=R){
			int u=q[L++];
			for(int i=fir[u],v;i;i=nxt[i]) if(typ[v=to[i]]!=2&&find(u)!=find(v)){
				if(!typ[v]){
					typ[v]=2,pre[v]=u;
					if(mat[v]) typ[q[++R]=mat[v]]=1;
					else {while(v) {int t=mat[pre[v]]; mat[mat[v]=pre[v]]=v,v=t;} return;}
				}
				else {int lca=LCA(u,v); blossom(u,v,lca),blossom(v,u,lca);}
			}
		}
	}
}
namespace Dinic{
	const int N = 50005;
	int S,T,dis[N],q[maxn],L,R,fir[N],cur[N],nxt[maxm],to[maxm],c[maxm],tot=1;
	inline void line(int x,int y){
		nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,c[tot]=1;
		nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,c[tot]=0;
	}
	void init(int sz){
		S=0,T=sz+1,memset(fir,0,(T+1)<<2),tot=1;
	}
	bool bfs(){
		memset(dis,-1,(T+1)<<2),q[L=R=1]=T,dis[T]=0;
		while(L<=R){
			int u=q[L++];
			for(int i=fir[u],v;i;i=nxt[i]) if(c[i^1]&&dis[v=to[i]]==-1) dis[v]=dis[u]+1,q[++R]=v;
		}
		return ~dis[S];
	}
	int aug(int u,int augco){
		if(u==T) return augco;
		int need=augco,delta;
		for(int &i=cur[u];i;i=nxt[i]) if(c[i]&&dis[to[i]]+1==dis[u]){
			delta=aug(to[i],min(c[i],need)),c[i]-=delta,c[i^1]+=delta;
			if(!(need-=delta)) break;
		}
		return augco-need;
	}
	void dinic(){
		while(bfs()) memcpy(cur,fir,(T+1)<<2),aug(S,1e9);
		for(int i=1,sz=A.size();i<=sz;i++) for(int j=fir[i];j;j=nxt[j]) if(!c[j]&&to[j]) mat[A[i-1]]=B[to[j]-sz-1],mat[B[to[j]-sz-1]]=A[i-1];
	}
}
using Dinic::S; using Dinic::T;
inline void print(int x,int y){
	printf("%d ",1+bool(y));
	for(int i=0;i<n;i++) if(x>>i&1) putchar(i+'a');
	if(y) {putchar(' ');for(int i=0;i<n;i++) if(y>>i&1) putchar(i+'a');}
	putchar('\n');
}
int main()
{
	scanf("%d%d",&n,&k),N=1<<n;
	for(int i=1;i<N;i++) bit[i]=bit[i>>1]+(i&1);
	for(int t=1;t<=k/2;t++){
		A.clear(),B.clear(); int sA=0,sB=0;
		for(int i=1;i<N;i++) if(bit[i]==t) A.push_back(i),id[i]=++sA;
		for(int i=N-1;i>=1;i--) if(bit[i]==k-t) B.push_back(i),id[i]=sA+(++sB);
		for(int i:A) if(!mat[i])
			for(int j:B) if(!mat[j]&&!(i&j)){
				mat[i]=j,mat[j]=i; break;
			}
		if(t*2==k){
			for(int i:A) for(int j:B) if(!(i&j)) Blossom::line(i,j);
			for(int i:A) if(!mat[i]) Blossom::aug(i);
		}
		else{
			Dinic::init(sA+sB);
			for(int i:A) if(!mat[i]) Dinic::line(S,id[i]); else Dinic::line(id[i],S);
			for(int i:B) if(!mat[i]) Dinic::line(id[i],T); else Dinic::line(T,id[i]);
			for(int i:A){
				int s=N-1-i;
				for(int j=s;j;j=(j-1)&s) if(bit[j]==k-t) mat[i]==j?Dinic::line(id[j],id[i]):Dinic::line(id[i],id[j]);
			}
			Dinic::dinic();
		}
	}
	int ans=0;
	for(int i=1;i<N;i++) if(bit[i]<=k&&i>mat[i]) ans++;
	printf("%d\n",ans);
	for(int i=1;i<N;i++) if(bit[i]<=k&&i>mat[i]) print(i,mat[i]);
}

然后被随机匈牙利爆c。。
example(Code by LanrTabe):

#include <bits/stdc++.h>

const int N=1<<17;
std::mt19937 Rand(0721);
int n,k,Mat[N],Vis[N];
std::vector<int> v[20],G[N];
std::vector<std::pair<int,int> > Ans;

inline void Out(int x){for(int i=0;i<n;++i)if(x>>i&1)putchar('a'+i);}

bool DFS(int x)
{
	std::shuffle(G[x].begin(),G[x].end(),Rand),Vis[x]=1;
	for(int i=0,y,v;i<(int)G[x].size();++i)if(!Vis[Mat[y=G[x][i]]])
	{
		if(v=Mat[y],Mat[x]=y,Mat[y]=x,!v||DFS(v))return true;
		Mat[v]=y,Mat[y]=v,Mat[x]=0;
	}
	return false;
}

void Link(std::vector<int>& L,int v,int s,int c,int r){
	if(r>=0&&r+c<=n)c!=n?Link(L,v,s,c+1,r),s>>c&1?Link(L,v|1<<c,s,c+1,r-1):void():L.push_back(v);}

void Match(std::vector<int>& u,std::vector<int>& v)
{
	std::vector<int> s;
	s.reserve(u.size()+v.size());
	for(int i:u)s.push_back(i);
	for(int i:v)s.push_back(i);
	for(int i:s)Link(G[i],0,((1<<n)-1)^i,0,k-__builtin_popcount(i));
	for(int c;;)
	{
		std::shuffle(s.begin(),s.end(),Rand),c=0;
		for(int i:s)Vis[i]=0;
		for(int i:s)if(!Mat[i])DFS(i);
		for(int i:u)c+=!Mat[i];
		if(!c||(&u==&v&&c==int(u.size()&1)))break;
	}
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1,j;i<1<<n;++i)if((j=__builtin_popcount(i))<=k)v[j].push_back(i);
	for(int i=1;i<=k-i;++i)Match(v[i],v[k-i]);
	for(int i=1;i<1<<n;++i)if(__builtin_popcount(i)<=k)
		{if(Mat[i]>i)Ans.emplace_back(i,Mat[i]);
		else if(!Mat[i])Ans.emplace_back(i,0);}
	printf("%d\n",(int)Ans.size());
	for(auto i:Ans)
	{
		printf("%d ",1+(i.second>0)),Out(i.first),putchar(i.second?' ':'\n');
		if(i.second)Out(i.second),putchar('\n');
	}
	return 0;
}

然而还有更快更短的神奇暴力更换匹配(%Freopen):

#include<bits/stdc++.h>
#define maxn 200005
using namespace std;

int n,K,bit[maxn],mat[maxn];
vector<int>G[20];
vector<vector<int> >ans;

void print(int x){
	for(int i=0;i<n;i++) if(x>>i&1) putchar('a'+i);
}

int main(){
	
//	freopen("1.in","r",stdin);
//	freopen("1.out","w",stdout);
	
	scanf("%d%d",&n,&K);
	for(int i=1;i<(1<<n);i++){
		bit[i] = bit[i>>1] + (i&1);
		G[bit[i]] .push_back(i);
	}
	for(int i=0;i<=K-i;i++){
		set<int>st;
		for(int j=0;j<G[i].size();j++)
			st.insert(G[i][j]);
	//	random_shuffle(G[K-i].begin(),G[K-i].end());
		int l = 0;
		for(int u;!st.empty();){
			if(i == K-i && st.size() == 1) break;
			u=*st.rbegin();
			for(;;l++){
				if(l == G[K-i].size()) l = 0;
				if((G[K-i][l] & u) == 0){
					int v = G[K-i][l];
					if(mat[v]) mat[mat[v]] = 0 , st.insert(mat[v]);
					else st.erase(v);
					mat[u] = v , mat[v] = u;
					st.erase(u);
					l++;
					break;
				}
			}
		}
	}
	for(int i=1;i<(1<<n);i++) if(bit[i] <= K){
		if(mat[i]){
			if(mat[i] > i){
				vector<int>r;
				r.push_back(i),r.push_back(mat[i]);
				ans.push_back(r);
			}
		}
		else{
			vector<int>r;
			r.push_back(i);
			ans.push_back(r);
		}
	}
	printf("%d\n",ans.size());
	for(int i=0;i<ans.size();i++){
		printf("%d",ans[i].size());
		for(int j=0;j<ans[i].size();j++)
			putchar(' '),print(ans[i][j]);
		puts("");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值