题目链接:http://codeforces.com/problemset/problem/468/B
题目大意:给你N个互不相同的整数,再给两个整数a,b,问能否把这N个数分成两部分,使得在a这个集合中的任一个数ai,都存在a-ai也在a 这个集合中,对b集合也同理。如果不存在一种合理分法,输出No,否则输出YES,并输出任一种合理分组。
解题思路:
首先我们先假设存在一种合理的方案,则有 对于任一个数x,肯定在a,b集合中的一个。同时如果方案合理,最后的结果a,b集合中的元素没有交集。
1.对于一个数x,如果a-x不存在,则x肯定在b分组中(假定分组成立),则把x和b分组并起来;如果同时b-x也不存在的话,则再把x和a分组并起来,此时a和b分组被并了起来,显然这种方案不合理。
2.对于一个数x,如果a-x存在,则把x和a-x对应的下标并起来,此时并不能说明x属于集合a,因为可能存在N=2,a=8,b=20,4,16,这样的数据,只有当b-x不存在时,才把x和a的下标并起来。
3,如果b-x也存在时,就把x和b-x也的下标也并起来,此时如果x!=a-x!=b-x时,说明a和b集合公用x,即这种方案a和b被并起来,这种方案也不合理。此时如果x==a-x!=b-x,显然应分组为b,当然特例就是a和b相等,此时任何分组都可以。
总的情况应该这几种。
对于代码,有以下几点感觉很好:
1,map映射,使得查找成对的数字查找更方便。
2,虚拟出两个a,b集合的根结点n+1和n+2;
3,并查集的特殊处理;
代码如下:
#include<stdio.h>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
#define N 100005
int f[N],num[N];
map <int ,int> mp;
int find(int x) {return x!=f[x]?f[x]=find(f[x]):f[x];}
void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
f[fx]=fy;
return;
}
int main()
{
int n,a,b;
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n+2;i++)
f[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%d",&num[i]);
mp[num[i]]=i;
}
for(int i=1;i<=n;i++)
{
if(mp[a-num[i]]) merge(i,mp[a-num[i]]);
else merge(i,n+2);
if(mp[b-num[i]]) merge(i,mp[b-num[i]]);
else merge(i,n+1);
}
if(find(n+1)==find(n+2))
printf("NO\n");
else
{
printf("YES\n");
for(int i=1;i<=n;i++)
{
if(find(i)==find(n+1)) printf("0 ");
else printf("1 ");
}
printf("\n");
}
return 0;
}