[WC2016]挑战NPC

只想感叹构图太厉害了

把每个筐拆成三个点连成三元环,对于每个限制条件,把球代表的点连三条边到筐的三个点,匹配数减去$n$就是答案

下面解释为什么是这样

如果某个筐没放球,那么这个筐的三个点最大匹配数为$1$(三元环选一条边匹配)

如果某个筐放$1$个球,那么最大匹配是$2$(球匹配一个点,未被球匹配的两点互相匹配)

如果某个筐放$2$个球,那么最大匹配是$2$

如果某个筐放$3$个球,那么最大匹配是$3$

因为只有筐是半空时,它的内部会有$1$条匹配边,而这个建模使得每个筐的匹配数减去放球数恰好等于(筐是否半空),所以答案就是匹配数$-n$

应该先从球代表的点开始增广,再从筐代表的点开始增广,这既保证了每个球都被放入,又保证了筐内的匹配数尽可能大

然后就做完了,这个建模的巧妙思路实在是太棒了==

#include<stdio.h>
#include<string.h>
int h[610],to[200010],nex[200010],q[200010],fa[610],type[610],pre[610],match[610],tm[610],M,n,head,tail;
void add(int a,int b){
	M++;
	to[M]=b;
	nex[M]=h[a];
	h[a]=M;
}
void insert(int x,int y){
	add(x,y);
	add(y,x);
}
void swap(int&a,int&b){a^=b^=a^=b;}
int get(int x){return(fa[x]==x)?x:(fa[x]=get(fa[x]));}
int lca(int x,int y){
	M++;
	while(1){
		if(x){
			x=get(x);
			if(tm[x]==M)return x;
			tm[x]=M;
			x=pre[match[x]];
		}
		swap(x,y);
	}
}
void blossom(int x,int y,int p){
	while(get(x)!=p){
		pre[x]=y;
		y=match[x];
		if(type[y]==2){
			type[y]=1;
			tail++;
			q[tail]=y;
		}
		if(fa[x]==x)fa[x]=p;
		if(fa[y]==y)fa[y]=p;
		x=pre[y];
	}
}
int bfs(int x){
	int i,now,las;
	for(i=1;i<=n;i++)fa[i]=i;
	memset(pre,0,sizeof(pre));
	memset(type,0,sizeof(type));
	head=tail=1;
	q[1]=x;
	type[x]=1;
	while(head<=tail){
		x=q[head];
		head++;
		for(i=h[x];i;i=nex[i]){
			if(type[to[i]]==2||get(to[i])==get(x))continue;
			if(type[to[i]]==0){
				type[to[i]]=2;
				pre[to[i]]=x;
				if(match[to[i]]==0){
					now=to[i];
					while(now){
						las=match[pre[now]];
						match[now]=pre[now];
						match[pre[now]]=now;
						now=las;
					}
					return 1;
				}
				type[match[to[i]]]=1;
				tail++;
				q[tail]=match[to[i]];
			}else{
				now=lca(x,to[i]);
				blossom(x,to[i],now);
				blossom(to[i],x,now);
			}
		}
	}
	return 0;
}
int main(){
	int T,n,m,e,i,x,y;
	scanf("%d",&T);
	while(T--){
		memset(h,0,sizeof(h));
		memset(match,0,sizeof(match));
		memset(tm,0,sizeof(tm));
		M=0;
		scanf("%d%d%d",&n,&m,&e);
		for(i=1;i<=m;i++){
			insert(i*3-2,i*3-1);
			insert(i*3-1,i*3);
			insert(i*3,i*3-2);
		}
		while(e--){
			scanf("%d%d",&x,&y);
			insert(x+m*3,y*3-2);
			insert(x+m*3,y*3-1);
			insert(x+m*3,y*3);
		}
		::n=m*3+n;
		x=M=0;
		for(i=::n;i>0;i--){
			if(match[i]==0)x+=bfs(i);
		}
		printf("%d\n",x-n);
		for(i=3*m+1;i<=::n;i++)printf("%d ",(match[i]-1)/3+1);
	}
}

转载于:https://www.cnblogs.com/jefflyy/p/8643334.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值