集合
【问题描述】
小明非常喜欢数字。最近他的妈妈送给他了一个由n个非负整数组成的集合。小明非常喜欢和小刚玩。他立即决定把他n个数字中的一部分送给小刚。为了让游戏更加有趣,小明决定使得给她的数字集合满足如下条件:
我们用x1表示小明的数字集合的xor值,用x2表示小刚的数字集合的xor值。要使得x1+x2尽可能地大。假如有多种划分集合的方法使得集合满足上述条件,小明就要让x1尽可能地小。
帮助小明照上述方法划分集合。如果有多种合适的方法,就找出其中任意一种方法。请注意,小明将一部分数字给了小刚之后,他可能就没有任何剩余的数字了。反之亦然,小明也可以不给小刚任何数字。在这两种情况下,我们都假定空集的xor值为0。
【输入格式】
从文件set.in中输入数据。
输入的第一行包含一个整数n,表示小明的妈妈给了他多少个数字。
第二行包含n个用空格隔开的数字,保证它们都是不超过10^18的非负整数。
【输出格式】
输出到文件set.out中。
输出一行,包含n个用空格隔开的整数,如果第i个数是1,则表示小明保留给出的第i个数字,如果第i个数是2,则表示小明把给出的第i个数字给了小刚。
【样例输入】
8
1 1 2 2 3 3 4 4
【样例输出】
1 2 1 2 2 2 1 2
【数据规模与约定】
对于30%的数据,保证n<=10;
对于60%的数据,保证n<=1000;
对于100%的数据,保证n<=100000。
这道题需要按位考虑。如果总体异或和的某一位是0,那么肯定分成的x1和x2这一位都是1。
先考虑为0的,后考虑为1的是最优策略。
相当于每一步我们都扔进去一个异或方程组,直接消元,线性相关肯定就扔掉了。
用bitset压位一下就好了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define M 100010 4 #define LL long long 5 inline LL read() { 6 char ch = getchar(); LL x = 0, f = 1; 7 while(ch < '0' || ch > '9') { 8 if(ch == '-') f = -1; 9 ch = getchar(); 10 } 11 while('0' <= ch && ch <= '9') { 12 x = x * 10 + ch - '0'; 13 ch = getchar(); 14 } 15 return x * f; 16 } 17 LL A[M]; 18 bitset<100010> a[110]; 19 int pos[M]; 20 int res[M]; 21 int tot; 22 int main() { 23 int n; scanf("%d", &n); 24 LL All = 0; 25 for(int i = 0; i < n; ++ i) { 26 A[i] = read(); 27 All ^= A[i]; 28 } 29 for(int k = 0; k < 2; ++ k) { 30 for(int i = 60; i >= 0; -- i) if((All >> i & 1) == k){ 31 ++ tot; 32 for(int j = 0; j < n; ++ j) if((A[j] >> i & 1)){ 33 a[tot][j] = 1; 34 } 35 a[tot][n] = k ^ 1; 36 for(int j = 1; j < tot; ++ j) { 37 if(a[tot][pos[j]]) a[tot] ^= a[j]; 38 } 39 pos[tot] = -1; 40 for(int j = 0; j < n; ++ j) { 41 if(a[tot][j]) { 42 pos[tot] = j; 43 break; 44 } 45 } 46 if(pos[tot] == -1) { 47 -- tot; continue; 48 } 49 for(int j = 1; j < tot; ++ j) { 50 if(a[j][pos[tot]]) a[j] ^= a[tot]; 51 } 52 } 53 } 54 for(int i = 1; i <= tot; ++ i) { 55 res[pos[i]] = a[i][n]; 56 } 57 for(int i = 0; i < n; ++ i) { 58 printf("%d ", 2 - res[i]); 59 } 60 }