CF468B Two Sets
题意翻译
给出 nn 个各不相同的数字,将它们分别放入 AA 和 BB 两个集合中,使它们满足:
- 若数字 xx 在集合 AA 中,那么数字 a-xa−x 也在集合 AA 中;
- 若数字 xx 在集合 BB 中,那么数字 b-xb−x 也在集合 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;
}