NC15167 集合问题(并查集)

这道题目思维量还是挺大的,因为这道题也是维护集合关系,所以我们想到用并查集去维护。

我们应该想到的是,用map映射一下位置,因为原来的数据比较大,不宜维护并查集关系,并且设计两个原点 0 和n+1,表示ab集合,这招是常见手段,因为这两个点是特殊的。之前有到异或并查集也是设计一个另外的原点

又因为x和a-x要是一体的,所以如果x和a-x都存在,那么我们就把他们合并一下,如果不存在,那么证明x必须要去b集合,因此我们将它和n+1合并

x和b-x同理。那么合并完后,如果出现0和n+1的父亲节点一样,说明至少有一个点两个集合都要在,所以输出no

否则,我们枚举看他属于a还是b集合,这里就出现了最关键的一点,也就是有些点两个集合都可以在,所以优先考虑b

这是什么情况呢,比如x1,x2,x3,x4

a-x1=x2

b-x1=x3

a-x4=x3

b-x4=x2

类似这样的情况。所以这些点既没有和0点相连,也没有和n+1相连,所以在判断的时候,要先判断a,只有必须在a的才是a,其他都是b

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<map>
using namespace std;
const int N=1e5+5;
map<int,int> m1;
int p[N];
int s[N];
int find(int x){
    if(x!=p[x]){
        p[x]=find(p[x]);
    }
    return p[x];
}
void add(int a,int b){
    int pa=find(a);
    int pb=find(b);
    if(pa!=pb){
        p[pa]=pb;
    }
}
int main(){
    int n,a,b;
    cin>>n>>a>>b;
    int i;
    for(i=0;i<=n+1;i++){
        p[i]=i;
    }
    for(i=1;i<=n;i++){
        cin>>s[i];
        m1[s[i]]=i;
    }
    for(i=1;i<=n;i++){
        int x=m1[b-s[i]];
        int y=m1[a-s[i]];
        if(x) add(x,i);
        else add(i,0);
        if(y) add(y,i);
        else add(i,n+1);
    }
    int pa=find(0);
    int pb=find(n+1);
    if(pa==pb){
        cout<<"NO"<<endl;
    }
    else{
        cout<<"YES"<<endl;
        for(i=1;i<=n;i++){
            if(pa==find(i))
                cout<<"0";
            else
                cout<<"1";
            if(i!=n)
                cout<<" ";
            else
                cout<<endl;
        }
    }
    return 0;
}
View Code

 

posted @ 2020-04-11 19:27  朝暮不思  阅读(176)  评论(0编辑  收藏  举报