【bzoj3687】bitset 的巧妙应用

29 篇文章 0 订阅

【题目描述】
小呆开始研究集合论了,他提出了关于一个数集四个问题:
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[n]
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

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值