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: 由乃的玉米田