CF468B Two Sets

CF468B Two Sets

洛谷传送门

题意翻译

给出 nn 个各不相同的数字,将它们分别放入 AA 和 BB 两个集合中,使它们满足:

  • 若数字 xx 在集合 AA 中,那么数字 a-xax 也在集合 AA 中;
  • 若数字 xx 在集合 BB 中,那么数字 b-xbx 也在集合 BB 中。 输入格式:

输入的第一行输入三个整数 n,a,b (1 leq n leq 10^{5} ; 1 leq a,b leq 10^{9} )n,a,b(1≤n≤105;1≤a,b≤109)。

输入的第二行有 nn 个各不相同的正整数,且每个正整数的数值大小都在 [1,10^{9}][1,109] 内。 输出格式:

若不能能将这 nn 个数在满足条件的情况下全部放入 AA 和 BB 这两个集合中,则输出 NO

若这 nn 个数在满足条件的情况下能被全部放入 AA 和 BB 这两个集合中,则第一行输出 YES ,第二行输出 nn 个数 00 或 11 ,第 ii 个数为 00 表示第 ii 个数在集合 AA 中,第 ii 个数为 11 表示第 ii 个数在集合 BB 中。


题解:

很容易看出来这道题最难搞的是冲突的处理,也就是a-x,b-x和x同时存在于原数列的情况。

于是就有了思路(比较清奇,因为是先想到什么东西难搞,然后再回推思路)

也就是:

对于一个(x)来说,能分成以下4种情况进行分类讨论:

1.(a-x)不存在,(b-x)不存在。这种情况直接NO。

2.(a-x)存在,(b-x)不存在。这种情况是集合A。

3.(a-x)不存在,(b-x)存在。这种情况是集合B。

4.(a-x)存在,(b-x)存在。

第四种情况就是着重要讨论的冲突情况。

那么我们接下就需要再找到一个证据来唯一确定这个数的从属集合。也就是:还需要判断是否存在(y)(a-y=b-x)或者(b-y=a-x)其中之一存在。

如果存在(a-y=b-x),那么就把(x)(a-x)(y)(a-y(b-x))都放到集合A中。同理(b-y=a-x)存在的情况。如果都不存在就输出NO。

然后我们采用并查集来维护这个东西。

我还写了个版本是(set)直接模拟维护的,但是挂了,,,有兴趣的同学请去讨论区康康,感谢抓虫。

代码:

#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
const int maxn=1e5+5;
int maxx,n,a,b,fa[maxn],val[maxn];
map<int,int> mp;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge(int x,int y)
{
	int fx=find(x),fy=find(y);
	if(fx!=fy)
		fa[fx]=fy;
}
int main()
{
	scanf("%d%d%d",&n,&a,&b);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&val[i]);
		mp[val[i]]=i;
		maxx=max(maxx,val[i]);
		fa[i]=i;
	}
	fa[0]=0;
	fa[n+1]=n+1;
	if(maxx>=max(a,b))
	{
		printf("NO");
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		if(mp[a-val[i]]>0) 
			merge(i,mp[a-val[i]]);
		else 
			merge(i,n+1);
		if(mp[b-val[i]]>0) 
			merge(i,mp[b-val[i]]);
		else 
			merge(i,0);
	}
	int l=find(0),r=find(n+1);
	if(l!=r)
	{
		puts("YES");
		for(int i=1;i<=n;i++)
		{
			if(i!=1) 
				printf(" ");
			if(find(i)==l) 
				printf("0");
			else 
				printf("1");
		}
	}
	else 
		puts("NO");
	return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/14018232.html