线段树上维护状压(位运算)
洛谷P1558
分析:
颜色类型只有 \(30\) 种,可以利用二进制进行状压。
线段树维护一个二进制数表示区间的颜色为哪一种,将这个区间的颜色进行状压,每一种颜色对应二进制数的某一位。合并区间时将两个子节点的数按位或即可,题目区间修改为直接覆盖,统计答案时只需统计对应区间的数有多少个 \(1\) 即可。
代码:
折叠/打开
#include <cstdio>
#include <iostream>
#define maxn 4000005
using namespace std;
int l, r, k, p, T;
char check;
int n, q, a, b, c;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x * f;
}
struct Tree {
int l, r, ans, xr, lazy, len;
} t[maxn << 2];
inline void update(int node) { t[node].ans = t[node << 1].ans | t[node << 1 | 1].ans; }
inline void pushdown(int node) {
if (t[node].lazy) {
t[node << 1].lazy = t[node << 1 | 1].lazy = t[node].lazy;
t[node << 1].ans = t[node << 1 | 1].ans = 1 << t[node].lazy;
t[node].lazy = 0;
}
}
inline void build(int l, int r, int node) {
t[node].l = l, t[node].r = r, t[node].len = r - l + 1;
if (l == r) {
t[node].ans = 2;
return;
}
//初始类型都为1
int mid = l + r >> 1;
build(l, mid, node << 1);
build(mid + 1, r, node << 1 | 1);
update(node);
}
inline void change(int l, int r, int k, int node) {
if (l <= t[node].l && t[node].r <= r) {
t[node].ans = (1 << k);
t[node].lazy = k;
return;
}
//因为是染色所以直接覆盖
int mid = t[node].l + t[node].r >> 1;
pushdown(node);
if (l <= mid)
change(l, r, k, node << 1);
if (mid < r)
change(l, r, k, node << 1 | 1);
update(node);
}
inline int ask(int l, int r, int node) {
if (l <= t[node].l && t[node].r <= r) {
return t[node].ans;
}
pushdown(node);
int ans = 0;
int mid = t[node].l + t[node].r >> 1;
if (l <= mid)
ans |= ask(l, r, node << 1);
if (mid < r)
ans |= ask(l, r, node << 1 | 1);
return ans;
}
int main() {
n = read(), q = read();
T = read();
build(1, n, 1);
while (T--) {
int x, y, z;
char op;
cin >> op >> x >> y;
if (x > y)
swap(x, y);
if (op == 'C') {
z = read();
change(x, y, z, 1);
} else {
int otto = ask(x, y, 1);
int ans = 0;
for (int i = 1; i <= q; i++)
if (otto & (1 << i))
ans++;
//处理出有多少个1即为不同的种类数
printf("%d\n", ans);
}
}
return 0;
}
LYOI #1194 League of Legends
题目大意:
有\(n\)个棋盘,每个棋盘初始有一个类型为\(1\)的棋子,最多有\(28\)种棋子,对于棋盘有四种操作。
- 在 \([l,r]\) 号棋盘中,若没有 \(p\) 类型的棋子,则放入一枚 \(p\) 类型的棋子。
- 表示在第 \(k\) 号棋盘中,若有 \(p\) 类型的棋子,则将其移除。
- 表示询问第 \([l,r]\) 号棋盘中,有几种类型的棋子出现了奇数次。
- 表示询问第 \([l,r]\) 号棋盘中,有几种类型的棋子在每一个棋盘中都出现过。
分析(重点在对应的代码讲解):
对于位运算的前置知识:
x&1 如果答案为1则x为奇数,如果答案为0则x为偶数
a&=(b^a)就是将a中所有为1的位改成0
线段树上二进制状压,思路和上题类似。
线段树每个节点维护两个值 \(ans,xr\)。
\(ans\)用来维护棋子 \(p\) 在区间每一个棋盘内是否都出现过;\(xr\) 维护棋子出现次数的奇偶。
build建树:每个节点初始为类型为 \(1\)
update合并区间:将左右子区间的 \(ans\) 按位与起来,因为是每一个区间都要出现所以用按位与;将左右子区间的 \(xr\) 异或,偶数为 \(0\) 奇数为 \(1\) ,可保留出现的奇数次数。
折叠/打开
inline void update(int node) {
t[node].xr = t[node << 1].xr ^ t[node << 1 | 1].xr;
t[node].ans = t[node << 1].ans & t[node << 1 | 1].ans;
}
pushdown下传标记:子区间懒标记异或上当前节点懒标记向下传递;判断区间长度是否为奇数,如果区间为奇数,则满足询问奇数操作,就把子区间 \(xr\) 异或上懒标记,下传到子节点。如果为偶数且被某种棋子覆盖,对答案没有影响,所以将偶数区间内打上标记的棋子删除。
折叠/打开
inline void pushdown(int node) {
t[node << 1].lazy |= t[node].lazy;
t[node << 1 | 1].lazy |= t[node].lazy;
if (t[node << 1].len & 1)
t[node << 1].xr |= t[node].lazy;
else
t[node << 1].xr &= (t[node].lazy ^ t[node << 1].xr);
if (t[node << 1 | 1].len & 1)
t[node << 1 | 1].xr |= t[node].lazy;
else
t[node << 1 | 1].xr &= (t[node].lazy ^ t[node << 1 | 1].xr);
t[node << 1].ans |= t[node].lazy;
t[node << 1 | 1].ans |= t[node].lazy;
t[node].lazy = 0;
}
修改change:\([l,r]\) 中没有 \(p\) 类的棋子就放入一枚。所以在所以对于区间 \(ans\) 要或上第\(p\)位的\(1\),代表加入了 \(p\) 类型的棋子。
删除change_p:\(k\)号棋盘中若有 \(p\) 类棋子就将其删除。所以对于区间的\(ans\)和\(xr\)都要与上\(p\)位的\(0\),表示将\(p\)类棋子删除。
询问ask_1:询问区间内有多少种棋子出现奇数次。如果两个子区间内\(p\)这个棋子都出现了奇数次(则该位为\(1\))或偶数次(则该位为\(0\)),那么总的区间\(q\)这个棋子总共就出现了偶数次。所以为了只统计奇数的棋子,需要在统计左或右区间时将答案进行异或。
询问ask_2:询问区间中有几种棋子在每个棋盘都出现过。与第一种询问同理,统计子区间答案时要将答案按位与。
全代码:
#include <cstdio>
#define lowbit(a) (a & (-a))
#define maxn 4000005
int l, r, k, p, check;
int n, q, a, b, c;
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x * f;
}
struct Tree {
int l, r, ans, xr, lazy, len;
} t[maxn << 2];
inline void update(int node) {
t[node].xr = t[node << 1].xr ^ t[node << 1 | 1].xr;
t[node].ans = t[node << 1].ans & t[node << 1 | 1].ans;
}
inline void pushdown(int node) {
t[node << 1].lazy |= t[node].lazy;
t[node << 1 | 1].lazy |= t[node].lazy;
if (t[node << 1].len & 1)
t[node << 1].xr |= t[node].lazy;
else
t[node << 1].xr &= (t[node].lazy ^ t[node << 1].xr);
if (t[node << 1 | 1].len & 1)
t[node << 1 | 1].xr |= t[node].lazy;
else
t[node << 1 | 1].xr &= (t[node].lazy ^ t[node << 1 | 1].xr);
t[node << 1].ans |= t[node].lazy;
t[node << 1 | 1].ans |= t[node].lazy;
t[node].lazy = 0;
}
inline void build(int l, int r, int node) {
t[node].l = l, t[node].r = r, t[node].len = r - l + 1;
if (l == r) {
t[node].xr = 2;
t[node].ans = 2;
return;
}
int mid = l + r >> 1;
build(l, mid, node << 1);
build(mid + 1, r, node << 1 | 1);
update(node);
}
inline void change(int l, int r, int k, int node){
if (l <= t[node].l && t[node].r <= r) {
t[node].ans |= k;
if (t[node].len & 1)
t[node].xr |= k;
else
t[node].xr &= (t[node].xr ^ k);
t[node].lazy |= k;
return;
}
int mid = t[node].l + t[node].r >> 1;
if (t[node].lazy)
pushdown(node);
if (l <= mid)
change(l, r, k, node << 1);
if (mid < r)
change(l, r, k, node << 1 | 1);
update(node);
}
inline void change_p(int p, int k, int node) {
if (t[node].l == t[node].r) {
t[node].ans &= k;
t[node].xr &= k;
return;
}
int mid = t[node].l + t[node].r >> 1;
if (t[node].lazy)
pushdown(node);
if (p <= mid)
change_p(p, k, node << 1);
else
change_p(p, k, node << 1 | 1);
update(node);
}
inline int ask_1(int l, int r, int node) {
if (l <= t[node].l && t[node].r <= r) {
return t[node].xr;
}
if (t[node].lazy)
pushdown(node);
int ans = 0;
int mid = t[node].l + t[node].r >> 1;
if (l <= mid)
ans = ask_1(l, r, node << 1);
if (mid < r)
ans ^= ask_1(l, r, node << 1 | 1);
return ans;
}
inline int ask_2(int l, int r, int node) {
if (l <= t[node].l && t[node].r <= r) {
return t[node].ans;
}
if (t[node].lazy)
pushdown(node);
int ans = 1073741823;
int mid = t[node].l + t[node].r >> 1;
if (l <= mid)
ans = ask_2(l, r, node << 1);
if (mid < r)
ans &= ask_2(l, r, node << 1 | 1);
return ans;
}
int main() {
freopen("lol.in", "r", stdin);
freopen("lol.out", "w", stdout);
n = read(), q = read();
build(1, n, 1);
while (q--) {
check = read();
if (check == 1) {
l = read(), r = read(), p = read();
change(l, r, (1 << p), 1);
} else if (check == 2) {
k = read(), p = read();
int kkksb = 1073741823;
change_p(k, (kkksb ^ (1 << p)), 1);
} else if (check == 3) {
l = read(), r = read();
int num = ask_1(l, r, 1), cnt = 0;
while (num) {//计算这个数有几个1
num -= lowbit(num);
++cnt;
}
printf("%d\n", cnt);
} else {
l = read(), r = read();
int num = ask_2(l, r, 1), cnt = 0;
while (num) {
num -= lowbit(num);
++cnt;
}
printf("%d\n", cnt);
}
}
return 0;
}