集合异或 \operatorname{集合异或} 集合异或
题目链接: ybtoj 20036 \operatorname{ybtoj\ 20036} ybtoj 20036
题目
tt 喜欢数据结构,可是他面前有一道难题,他一下子就秒切了,为了提高你的水平,他把这道题给了你:
给定一个长度为 n n n 的多重集 A A A,求出这个集合所有的非空子集中的所有元素和的异或和。
输入
第一行一个数 n n n。
然后 n n n 行,每行 1 1 1 个数可重集 A A A 中的一个元素。
注意使用 scanf 或 cin 输入,不要使用快读输入 。
输出
输出一行一个数表示答案。
样例输入1
2
1 3
样例输出1
6
样例解释1
{ 1 , 3 } \{1,3\} {1,3} 集合的所有非空子集, { 1 } , { 3 } , { 1 , 3 } \{1\},\{3\},\{1,3\} {1},{3},{1,3},答案: 1 ⊕ 3 ⊕ ( 1 + 3 ) = 6 1\oplus 3\oplus (1+3)=6 1⊕3⊕(1+3)=6。
⊕ \oplus ⊕ 指异或操作。
样例2~5
在本题附加文件中
数据范围
对于 20 % 20\% 20% 的数据,保证在样例中出现。
对于 100 % 100\% 100% 的数据, ∀ x ∈ A , x > 0 , 1 < n < 1000 , ∑ A i ≤ 2 × 1 0 6 \forall x\in A,x>0,1<n<1000,\sum A_i\leq 2\times 10^6 ∀x∈A,x>0,1<n<1000,∑Ai≤2×106。
提示
思路
这道题是一道数据结构体。
用 bitset 来做。
题目是要每个子集所有数的和异或起来,那我们可以用一个 bitset 数组
a
n
s
ans
ans 来记录和为
i
i
i 的集合们对答案有没有贡献。
(因为是异或,所以出现了偶数次就是没有贡献,被抵消了,否则就有贡献,抵消完之后剩下一个)
那每一次有一个新的数
x
x
x 就 ans ^= ans << x
(就是之前有的
1
1
1 全部都右移了
x
x
x 位,就是都加了
x
x
x,再跟原来不加的异或)
然后最后把 a n s ans ans 中是 1 1 1 的位的位异或起来,就是答案了。
代码
#include<cstdio>
#include<bitset>
using namespace std;
int n, a, sum, answer;
bitset <2000000> ans;
int main() {
// freopen("segment.in", "r", stdin);
// freopen("segment.out", "w", stdout);
scanf("%d", &n);
ans.set(0, 1);
for (int i = 1; i <= n; i++) {
scanf("%d", &a);
sum += a;
ans ^= ans << a;
}
for (int i = 1; i <= sum; i++)
answer ^= ans.test(i) * i;//把是1的位的位异或起来
printf("%d", answer);
fclose(stdin);
fclose(stdout);
return 0;
}