[bzoj 3687] 简单题:bitset,DP

总有些题的名字比较逗……诸如“简单题”、“绝世好题”等等。

题意:求一个可重数集的子集的算数和的异或和。元素个数n不超过1000个,所有数是正整数,总和不超过2000000。

两次碰到bitset,都没做出来,学习一下。事先知道这道题可以用bitset来做。

bitset是一种STL容器,即位向量,用bitset<N>来定义,N是位数。可以用string来初始化,注意string的最低位是bitset的最高位。
可以用[]运算符来访问或修改bitset中的某一位,也可以setresetflip,以及用test来查看某一位(可以扔出一个异常)。测试整个bitset是否为空用none,测试是否非空用any,数有多少个1用count。转换为其他形式有to_stringto_ulong。bitset重载了位运算符。

给了两个方面的数据范围。“n<1000”告诉我们枚举子集不靠谱,设计分治算法失败,那么能否从“总和不超过2000000”入手?如果我能知道,对于给定数k,有多少个子集的算数和等于k就好了。DP可以解决,类似于背包。(插入:刚学到用DP求解背包问题是个伪多项式算法。)但还是慢……

把转移方程写出来:
f[i][j] = f[i-1][j-a[i]] + f[i-1][j]

每一轮中,j由原来的与j-a[i]叠加转移而来。把a[i]固定,这个动作很有规律,能不能一起做?我想到了矩阵乘法(虽然关联不大,但转移矩阵正是描述这一类动作的),想到了平移,想到了位运算中的左移右移。妙哉!

#include <cstdio>
#include <bitset>
using namespace std;
const int MAX_S = 2000000;
bitset<MAX_S+1> f;
int main()
{
    int n, a, ans = 0;
    scanf("%d", &n);
    f.set(0);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &a);
        f ^= f << a;
    }
    for (int i = 1; i <= MAX_S; ++i)
        if (f[i])
            ans ^= i;
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值