ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

题解 P4258 【[WC2016]挑战NPC】

2021-01-30 20:32:17  阅读:137  来源: 互联网

标签:ch 匹配 int 题解 P4258 WC2016 rg find getchar


P4258 [WC2016]挑战NPC

题目链接

小 I 浅笑:“所以,等我领图灵奖吧!”

每个筐能装三个?那我把它拆成三个筐,每个筐就只能装一个了。
球和筐的对应关系可以抽象为边。把球放进筐可以看成两两匹配。

但是现在要考虑的是怎样做到满足“一个筐子(这里指的是题面里的筐)内有不超过 \(1\) 个球”这个限制条件。

观察拆出的点我们发现,对于一个三个点的奇环,要么就没有匹配,要么只有一个匹配,另外单出一个点向外匹配。

所以我们把一个筐拆出的三个点互相连边,算出最大匹配数后 \(-n\) 就行。

然后就是在一个有 \(n+3m\) 个点的图上跑带花树。

拆点方法因人而异,我这里选择了 \(+m\) 的倍数

多测记得清空需要清空的数组!!!

code

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
#define rg register
inline int read()
{
	rg int x=0,w=1;
	rg char ch=getchar();
	if(ch=='-') w=-1,ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	while(ch<='9'&&ch>='0') x=(x<<3)+(x<<1)+(ch-'0'),ch=getchar();
	return x*w;
}
int head[N],ver[N],nxt[N],tot;
void add(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
int fa[N];
int find(int x)
{
	int x_root=x;
	while(fa[x_root]!=x_root) x_root=fa[x_root];
	while(x!=x_root)
	{
		int tmp=fa[x];
		fa[x]=x_root; x=tmp;
	}
	return x_root;
}
int match[N],pre[N];
int vis[N],dfn[N],tim=0;
int n,m,e,sum=0;

int lca(int x,int y)
{
	++tim;
	x=find(x);y=find(y);
	while(tim!=dfn[x])
	{
		dfn[x]=tim;
		x=find(pre[match[x]]);
		if(y) swap(x,y);
	}
	return x;
}

queue<int> q;
void blossom(int x,int y,int w)
{
	while(find(x)!=w)
	{
		pre[x]=y; y=match[x];
		if(vis[y]==2) vis[y]=1,q.push(y);
		if(find(x)==x) fa[x]=w;
		if(find(y)==y) fa[y]=w;
		x=pre[y];
	}
}

bool solve(int s)
{
	for(rg int i=1;i<=sum;++i) fa[i]=i,vis[i]=pre[i]=0;
	while(!q.empty()) q.pop();

	q.push(s);
	vis[s]=1;
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		for(int i=head[x];i;i=nxt[i])
		{
			int y=ver[i];

			if(find(x)==find(y)||vis[y]==2) continue;
			if(!vis[y])
			{
				vis[y]=2; pre[y]=x;
				if(!match[y])
				{
					for(int k=y,p;k;k=p)
						p=match[pre[k]],match[k]=pre[k],match[pre[k]]=k;
					return 1;
				}
				vis[match[y]]=1,q.push(match[y]);
			}
			else
			{
				int w=lca(x,y);
				blossom(x,y,w);
				blossom(y,x,w);
			}
		}
	}
	return 0;
}
inline void add_(int x,int y)
{
	add(x,y);add(y,x);
}
int main()
{
	int T=read();
	while(T--)
	{
		memset(head,0,sizeof head);
		memset(match,0,sizeof match);
		memset(pre,0,sizeof pre);
		memset(vis,0,sizeof vis);
		n=read(),m=read(),e=read();
		/*1-n为各个球代表的点的编号,n-n+3m是筐代表点的编号*/
		sum=n+3*m;
		for(int i=1;i<=e;i++)
		{
			int x,y;
			x=read(),y=read();
			add_(x,n+y);
			add_(x,n+m+y);
			add_(x,n+(m<<1)+y);
		}
		for(int i=1;i<=m;i++)
		{
			add_(n+i,n+m+i);
			add_(n+m+i,n+(m<<1)+i);
			add_(n+i,n+(m<<1)+i);
		}
		int res=0;
		for(int i=1;i<=sum;i++)
			if(!match[i]) res+=solve(i);
		printf("%d\n",res-n);
		for(int i=1;i<=n;i++)
			printf("%d ",(match[i]-n-1)%m+1);
		printf("\n");
	}
	return 0;
}

标签:ch,匹配,int,题解,P4258,WC2016,rg,find,getchar
来源: https://www.cnblogs.com/IzayoiMiku/p/14350363.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有