(並查集狀態壓縮)CodeForces - 469D Two Sets
Little X has n distinct integers: p1,?p2,?...,?pn. He wants to divide all of them into two sets A and B. The following two conditions must be satisfied:
- If number x belongs to set A, then number a?-?x must also belong to set A.
- If number x belongs to set B, then number b?-?x must also belong to set B
Help Little X divide the numbers into two sets or determine that it‘s impossible.
Input
The first line contains three space-separated integers n,?a,?b (1?≤?n?≤?105; 1?≤?a,?b?≤?109). The next line contains n space-separated distinct integers p1,?p2,?...,?pn (1?≤?pi?≤?109).
Output
If there is a way to divide the numbers into two sets, then print "YES" in the first line. Then print n
If it‘s impossible, print "NO" (without the quotes).
Example
Input4 5 9Output
2 3 4 5
YESInput
0 0 1 1
3 3 4Output
1 2 4
NO
Note
It‘s OK if all the numbers are in the same set, and the other one is empty.
註意到如果x存在於A果a-x、b-x(如果存在)也必須存在在A,如果a-(b-x) 、b-(a-x)存在也必須在A……即所有數可以劃分為若幹個集合,每個集合中的元素必須同時屬於A或B。這可以通過並查集維護。如果某數x,a-x,b-x都不存在那麽這個數無法放到A、B任何一個,直接輸出NO即可。
接下來維護並查集的狀態,采用狀態壓縮,用兩位2進制數表示可否放到A、B集合中。對於每個數x,如果a-x存在則其可以放在集合A,如果b-x存在則其可以放在集合B。對每個並查集取其集合內所有可行狀態的交集。顯然並查集之間是不再存在互相影響的。如果每個並查集可行狀態非0就表明一定存在符合題意的分配方式,任取每個集合的任意可行分配即可。
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 #include <queue> 8 #include <set> 9 #include <map> 10 #include <list> 11 #include <vector> 12 #include <stack> 13 #define mp make_pair 14 //#define P make_pair 15 #define MIN(a,b) (a>b?b:a) 16 //#define MAX(a,b) (a>b?a:b) 17 typedef long long ll; 18 typedef unsigned long long ull; 19 const int MAX=1e5+5; 20 const int MAX_V=25; 21 const int INF=2e9+5; 22 const double M=4e18; 23 using namespace std; 24 const int MOD=1e9+7; 25 typedef pair<ll,int> pii; 26 const double eps=0.000000001; 27 #define rank rankk 28 map<int,int> par;//父親 29 map<int,int> rank;//樹的高度 30 //初始化n個元素 31 map<int,bool> chu; 32 map<int,int> st; 33 int an[MAX]; 34 void init(int n) 35 { 36 for(int i=1;i<n;i++) 37 { 38 par[i]=i; 39 rank[i]=0; 40 } 41 } 42 //查詢樹的根,期間加入了路徑壓縮 43 int find(int x) 44 { 45 if(!par[x]) 46 { 47 rank[x]=0; 48 return par[x]=x; 49 } 50 if(par[x]==x) 51 return x; 52 else 53 return par[x]=find(par[x]); 54 } 55 //合並x和y所屬的集合 56 void unite(int x,int y) 57 { 58 x=find(x); 59 y=find(y); 60 if(x==y) 61 return ; 62 if(rank[x]<rank[y]) 63 par[x]=y; 64 else 65 { 66 par[y]=x; 67 if(rank[x]==rank[y]) 68 rank[x]++; 69 } 70 } 71 //判斷x和y是否屬於同一個集合 72 bool same(int x,int y) 73 { 74 return find(x)==find(y); 75 } 76 int n,a,b; 77 int x[MAX]; 78 int main() 79 { 80 scanf("%d%d%d",&n,&a,&b); 81 for(int i=1;i<=n;i++) 82 { 83 scanf("%d",&x[i]); 84 chu[x[i]]=true; 85 } 86 for(int i=1;i<=n;i++) 87 st[x[i]]=0; 88 for(int i=1;i<=n;i++) 89 { 90 bool sta=false; 91 if(chu[a-x[i]]) 92 { 93 sta=true; 94 unite(x[i],a-x[i]); 95 st[x[i]]|=1; 96 } 97 if(chu[b-x[i]]) 98 { 99 sta=true; 100 unite(x[i],b-x[i]); 101 st[x[i]]|=2; 102 } 103 if(!sta) 104 return 0*printf("NO\n"); 105 } 106 for(int i=1;i<=n;i++) 107 { 108 st[find(x[i])]&=st[x[i]]; 109 } 110 for(int i=1;i<=n;i++) 111 if(!st[x[i]]) 112 return 0*printf("NO\n"); 113 printf("YES\n"); 114 for(int i=1;i<=n;i++) 115 { 116 if(st[find(x[i])]&1) 117 printf("0 "); 118 else 119 printf("1 "); 120 } 121 printf("\n"); 122 return 0; 123 }
(並查集\狀態壓縮)CodeForces - 469D Two Sets