题目地址:
https://www.luogu.com.cn/problem/P2574
题目描述:
AKN觉得第一题太水了,不屑于写第一题,所以他又玩起了新的游戏。在游戏中,他发现,这个游戏的伤害计算有一个规律,规律如下:
1.拥有一个伤害串,是一个长度为
n
n
n的只含字符0
和字符1
的字符串。规定这个字符串的首字符是第一个字符,即下标从
1
1
1开始。
2.给定一个范围
[
l
,
r
]
[l,~r]
[l, r],伤害为伤害串的这个范围内中字符1
的个数
3.会修改伤害串中的数值,修改的方法是把
[
l
,
r
]
[l,~r]
[l, r]中所有原来的字符0
变成1
,将1
变成0
。
AKN 想知道一些时刻的伤害,请你帮助他求出这个伤害。
输入格式:
输入的第一行有两个用空格隔开的整数,分别表示伤害串的长度
n
n
n,和操作的个数
m
m
m。
输入第二行是一个长度为
n
n
n的字符串
S
S
S,代表伤害串。
第
3
3
3到第
(
m
+
2
)
(m + 2)
(m+2)行,每行有三个用空格隔开的整数
o
p
,
l
,
r
op, l, r
op,l,r。代表第
i
i
i次操作的方式和区间,规则是:
若
o
p
=
0
op = 0
op=0,则表示将伤害串的
[
l
,
r
]
[l,~r]
[l, r]区间内的0
变成1
,1
变成0
。
若
o
p
=
1
op = 1
op=1,则表示询问伤害串的
[
l
,
r
]
[l,~r]
[l, r]区间内有多少个字符1
。
输出格式:
对于每次询问,输出一行一个整数,代表区间内1
的个数。
数据范围:
对于
10
%
10\%
10%的数据,保证
n
,
m
≤
10
n, m \leq 10
n,m≤10。
另有
30
%
30\%
30%的数据,保证
n
,
m
≤
2
×
1
0
3
n, m \leq 2 \times 10^3
n,m≤2×103。
对于
100
%
100\%
100%的数据,保证
2
≤
n
,
m
≤
2
×
1
0
5
2 \leq n, m \leq 2 \times 10^5
2≤n,m≤2×105,
0
≤
o
p
≤
1
0 \leq op \leq 1
0≤op≤1,
1
≤
l
≤
r
≤
n
1 \leq l \leq r \leq n
1≤l≤r≤n,
S
S
S中只含字符0
和字符1
。
可以用带懒标记的线段树,懒标记记录当前区间是否要整体异或 1 1 1。那么两个操作分别对应区间修改和区间求和。代码如下:
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int n, m;
char s[N];
int a[N];
struct Node {
int l, r;
int sum;
bool b;
} tr[N << 2];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u) {
auto &l = tr[u << 1], &r = tr[u << 1 | 1];
if (tr[u].b) {
l.sum = l.r - l.l + 1 - l.sum;
r.sum = r.r - r.l + 1 - r.sum;
l.b ^= 1;
r.b ^= 1;
tr[u].b = false;
}
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, false};
if (l == r) {
tr[u].sum = a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) {
tr[u].sum = tr[u].r - tr[u].l + 1 - tr[u].sum;
tr[u].b ^= 1;
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r);
if (r > mid) modify(u << 1 | 1, l, r);
pushup(u);
}
int sum(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
int res = 0;
if (l <= mid) res = sum(u << 1, l, r);
if (r > mid) res += sum(u << 1 | 1, l, r);
return res;
}
int main() {
scanf("%d%d", &n, &m);
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
build(1, 1, n);
while (m--) {
int op, l, r;
scanf("%d%d%d", &op, &l, &r);
if (op == 0) modify(1, l, r);
else printf("%d\n", sum(1, l, r));
}
}
每次操作时间复杂度 O ( log n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)。