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; }
没有人不辛苦,只有人不喊疼