您的位置:首页 > 其它

【bzoj3687】bitset 的巧妙应用

2018-01-29 11:06 288 查看
【题目描述】

小呆开始研究集合论了,他提出了关于一个数集四个问题:

1. 子集的异或和的算术和。

2. 子集的异或和的异或和。

3. 子集的算术和的算术和。

4. 子集的算术和的异或和。

目前为止,小呆已经解决了前三个问题,还剩下最后一个问题还没有解决,他决定把

这个问题交给你,未来的集训队队员来实现。

【输入格式】

从 xor.in 中输入数据

第一行,一个整数 n。

第二行,n 个正整数,表示 a1, a2, …, an

【输出格式】

输出到 xor.out 中

一行,包含一个整数,表示所有子集和的异或和。

【样例输入】

2

1 3

【样例输出】

6

【样例解释】

6 = 1 ⊗ 3 ⊗ (1 + 3)

【数据规模与约定】

数据分为 A,B,C 三类。

A 类数据 (20%) 保证:ai > 0,1 ≤ n ≤ 10。

B 类数据 (40%) 保证:ai > 0,1 ≤ n ≤ 1000,

∑ ai ≤ 10000。

C 类数据 (40%) 保证:ai > 0,1 ≤ n ≤ 1000,

∑ ai ≤ 2000000。

另外,不保证集合中的数满足互异性,即有可能出现 ai = aj 且 i ̸= j。

这个bitset用的,我只剩下Orz 了。

很巧妙的应用。

bs[i] 表示用这n个数能够组成的和为i的方案数的奇偶(奇数次为1,偶数次为0) .

每次加入一个数字x,就相当于bs中所有存在1的位置都会再加一次x,所以整体往左移位x个,然后再和原来的bs异或。 脑补一下就可以知道。

代码 很简单

#include<bits/stdc++.h>
using namespace std;

const int maxn = 2000000+11;
bitset<maxn>bs;
int main(){
int n;scanf("%d",&n);
bs[0]=1; int sum=0;
for(int i=0;i<n;i++){
int a;scanf("%d",&a);
bs^=(bs<<a);
sum+=a;
}
int ans=0;
for(int i=1;i<=sum;i++){
if(bs[i])
ans^=i;
}
printf("%d\n",ans);
return 0;
}


网上还有一道和这个本质一样的题目 。

Give you N numbers a[1]…a

and M numbers b[1]…b[m]

For each b[k], if we can find i,j a[i] + a[j] = b[k] or a[i] = b[k] , we say k is a good number.

And you should only output the number of good numbers.

0 < n, m, a[i], b[j] <= 200000

sample input

3 6


1 
3
 5



2
 4 
 5 
 7 
 8 
 9

sample output

4

b[1]…b[m] 2,4,5,7,8,9

2 = 1+1

4 = 1+3

5 = 5

8 = 3+5.

分析:和刚才那个很像,刚看这个题目的时候,我还想着要是和超过200000怎么处理,想了一会才明白过来,无所谓啊,超过就超过呗 ,反正我们也不取。

代码

#include<bits/stdc++.h>
using namespace std;

const int maxn = 200000+11;

bitset<maxn>bs;
int a[maxn],b[maxn];
int main(){
int n,m;scanf("%d%d",&n,&m);
bs[0]=1;
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
bs|=(bs<<a[i]);  // 这里改为| 就ojbk了
// cout<<bs<<endl;
}
int ans=0;
for(int i=1;i<=m;i++) {
scanf("%d",&b[i]);
if(bs[b[i]])
ans++;
}
printf("%d\n",ans);
return 0;
}

/*
3 6
1 3 5
2 4 5 7 8 9

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: