bitset 基础用法
operator []: 访问其特定的一位。
operator ==/!=: 比较两个 bitset 内容是否完全一样。
operator &/&=/|/| =/^/^=/~: 进行按位与/或/异或/取反操作。
bitset 只能与 bitset 进行位运算,若要和整型进行位运算,要先将整型转换为 bitset。
operator <>/<<=/>>=: 进行二进制左移/右移。
operator <>: 流运算符,这意味着你可以通过 cin/cout 进行输入输出。
成员函数
count(): 返回 true 的数量。
size(): 返回 bitset 的大小。
test(pos): 它和 vector 中的 at() 的作用是一样的,和 [] 运算符的区别就是越界检查。
any(): 若存在某一位是 true 则返回 true,否则返回 false。
none(): 若所有位都是 false 则返回 true,否则返回 false。
all():C++11,若所有位都是 true 则返回 true,否则返回 false。
set(): 将整个 bitset 设置成 true。
set(pos, val = true): 将某一位设置成 true/false。
reset(): 将整个 bitset 设置成 false。
reset(pos): 将某一位设置成 false。相当于 set(pos, false)。
flip(): 翻转每一位。(相当于异或一个全是 1 的 bitset)
flip(pos): 翻转某一位。
to_string(): 返回转换成的字符串表达。
to_ullong():C++11,返回转换成的 unsigned long long 表达。
_Find_first(): 返回 bitset 第一个 true 的下标,若没有 true 则返回 bitset 的大小。
_Find_next(pos): 返回 pos 后面(下标严格大于 pos 的位置)第一个 true 的下标,
若 pos 后面没有 true 则返回 bitset 的大小。
bitset 优化例题
CF633G
首先可以知道:可以将子树通过 dfn 序转化为序列。
有一个思路是:维护大约 \(200\) 个树状数组来存储每个质数在区间中出现次数。复杂度 \(O(200\times n\log n)\)。在某些网站上可以通过(什么oj我不说)。
换一个思路,有一个限制:\(m\leq 1000\),这意味着我们完全可以将区间内开一个大小为 \(1000\) 的数组代表这个数是否出现。可以用线段树维护这类信息, push_up
时只需要将左子树维护的数组和右子树维护的数组或起来。区间加上一个数 \(x\) 相当于平移 \(x\) 单位,并且超出 \(m\) 范围的要循环移位。
复杂度 \(O(nm\log n)\)。肯定过不去。甚至还不如之前做法。
但是我们发现:我们数组维护的全是 01 信息,可以用 bitset 来代替。push_up
直接 tree[p].s = (tree[ls].s | tree[rs].s)
,区间加直接 tree[p].s = (((tree[p].s) >> (m - x)) | (tree[p].s << x))
。
复杂度 \(O(\dfrac{nm\log n}{32})\)。只放部分代码:
struct edge {
int l, r, lazy;
bitset<1005> s;
}tree[N * 4];
void push_up(int p) {
tree[p].s = (tree[ls].s | tree[rs].s);
}
void down(int p, int x) {
tree[p].lazy += x; tree[p].lazy %= m;
tree[p].s = (((tree[p].s) >> (m - x)) | (tree[p].s << x));
}
void push_down(int p) {
if (tree[p].lazy) {
tree[p].lazy %= m;
down(ls, tree[p].lazy); down(rs, tree[p].lazy);
tree[p].lazy = 0;
}
}
P5670
这题和上一道例题如出一辙,因为只看后 \(m\) 位,那前面的位其实就没有用了!等价于模上 \(2^m\)。这道题维护异或信息就行了。只放部分代码:
struct edge {
int l, r, lazy;
bitset<1024> s;
}tree[N * 4];
void push_up(int p) {
tree[p].s = tree[ls].s ^ tree[rs].s;
}
void add(int p, int x) {
tree[p].lazy = (tree[p].lazy + x) % m;
tree[p].s = ((tree[p].s >> (1024 - x)) | (tree[p].s << x));
}
void push_down(int p) {
if (tree[p].lazy) {
add(ls, tree[p].lazy);
add(rs, tree[p].lazy);
tree[p].lazy = 0;
}
}
signed main() {
n = read(), m = read(), p = read(); m = (1 << m);
for (int i = 1; i <= n; i++) a[i] = read();
build(1, 1, n);
while (p--) {
int op, l, r, x;
op = read(), l = read(), r = read();
if (op == 1) {
x = read();
modify(1, l, r, x);
}
else {
ans.reset();
query(1, l, r);
int res = 0;
for (int i = 0; i <= 1023; i++) if (ans[i]) res ^= i;
wr(res & (m - 1));
putchar('\n');
}
}
}
习题:
- AT_abc258_g: [ABC258G] Triangle
- P3674: 小清新人渣的本愿
- P4688: 掉进兔子洞
- P5355: 由乃的玉米田