2018年全国多校算法寒假训练营练习比赛(第五场)集合问题

链接: https://www.nowcoder.com/acm/contest/77/D
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给你a,b和n个数p[i],问你如何分配这n个数给A,B集合,并且满足:

若x在集合A中,则a-x必须也在集合A中。

若x在集合B中,则b-x必须也在集合B中。

输入描述:

第一行 三个数 n a b  1<=n<=1e5  1<=a,b<=1e9
第二行 n个数 p1 p2 p3...pn 1<=pi<=1e9

输出描述:

如果可以恰好分开就输出第一行 YES
然后第二行输出 n个数 分别代表pi 是哪个集合的  0 代表是A集合 1代表是B 集合
不行就输出NO
放在哪个集合都可以的时候优先放B

示例1

输入

4 5 9
2 3 4 5

输出

YES
0 0 1 1
示例2

输入

3 3 4
1 2 4

输出

NO

对于一个x来说,能分成以下4种情况(这里a!=b):

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

2.a-x存在,b-x不存在。这种情况把x和a-x放在集合A中。

3.a-x不存在,b-x存在。这种情况把x和b-x放在集合B中。

4.a-x存在,b-x存在。这种情况比较我们就不能直接确定要放A还是要放B了。

那么我们接下还需要判断是否存在y有a-y=b-x或者b-y=a-x其中之一存在。为什么是其中之一呢?

假设有a-y=b-x和b-y=a-x同时存在,

那么将两式子相减有x-y=y-x -> x*2=y*2 -> x=y成立,则有a-y=a-x=b-x -> a=b矛盾。

所以上面两种情况只可能存在一种或都不存在。如果存在a-y=b-x,那么就把x和a-x和y和a-y(b-x)都放到集合A中。

同理b-y=a-x存在的情况。

如果都不存在,那么我们就无法决则把x和a-x和b-x放到集合A中还是集合B中,因为放到那一边都会剩下另一个无法去放,所以就输出NO。

这里用并查集来维护即可。

#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
const int MAX=1e5+5;
int n,a,b,p[MAX],fa[MAX];
map<int,int> mp;
int Find(int x)
{
    int r=x,t;
    while(r!=fa[r])
    {
        r=fa[r];
    }
    while(x!=r)
    {
        t=fa[x];
        fa[x]=r;
        x=t;
    }
    return r;
}
void Union(int u,int v)
{
    int uu=Find(u);
    int vv=Find(v);
    if(uu!=vv)
    {
        fa[uu]=v;;
    }
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    int Max=0;
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&p[i]);
        mp[p[i]]=i;
        Max=max(Max,p[i]);
    }
    if(Max>=max(a,b))
        printf("NO\n");
    else
    {
        for(int i=0; i<=n+1; i++)
        {
            fa[i]=i;
        }
        for(int i=1; i<=n; i++)
        {
            if(mp[a-p[i]]) Union(i,mp[a-p[i]]);
            else Union(i,n+1);
            if(mp[b-p[i]]) Union(i,mp[b-p[i]]);
            else Union(i,0);
        }
        int A=Find(0);
        int B=Find(n+1);
        if(A!=B)
        {
            printf("YES\n");
            for(int i=1; i<=n; i++)
            {
                if(i!=1)
                    printf(" ");
                if(Find(i)==A)
                    printf("0");
                else
                    printf("1");
            }
            printf("\n");
        }
        else
            printf("NO\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值