Codeforces 899F Letters Removing

链接

传送门

题意

给出一个长度为\(n(1 \leq n \leq 2 \cdot 10^5)\)的字符串,进行\(m(1 \leq m \leq 2 \cdot 10^5)\)次删除操作,每个删除操作将删除当前字符串\(l\)\(r\)间所有字符\(c\)。输出\(m\)次操作之后的字符串。

思路

对每种字符都用set存下出现的位置,用树状数组来计算位置\(x\)前有多少个未被删除的字符。对于每次删除操作,利用树状数组将位置\(l\)\(r\)转换为初始字符串中的位置,然后在set中删除并更新字符串和树状数组。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>

typedef long long LL;

using namespace std;

const int maxn = 200010;
const int maxm = 260;

int n, m;
int bit[maxn];
char str[maxn];
set<int> s[maxm];

inline int lowbit(int& x) {
return x & (-x);
}

int sum(int i) {
int res = 0;
while (i > 0) {
res += bit[i];
i -= lowbit(i);
}
return res;
}

void add(int i, int x) {
while(i <= n){
bit[i] += x;
i += lowbit(i);
}
return;
}

int getPos(int x) {
int L = x, R = n, m = x;
while (L <= R) {
m = (L + R) / 2;
if (sum(m) >= x) {
R = m - 1;
} else {
L = m + 1;
}
}
return L;
}

int main() {
scanf("%d%d", &n, &m);
scanf("%s", str + 1);
for (int i = 1; str[i]; ++i) {
s[str[i]].insert(i);
add(i, 1);
}
int l, r;
char tmp[5];
while (m--) {
scanf("%d%d%s", &l, &r, tmp);
l = getPos(l);
r = getPos(r);
auto lb = s[tmp[0]].lower_bound(l);
auto ub = s[tmp[0]].upper_bound(r);
while (lb != ub) {
add(*lb, -1);
str[*lb] = 0;
++lb;
}
s[tmp[0]].erase(s[tmp[0]].lower_bound(l), ub);
}
for (int i = 1; i <= n; ++i) {
if (str[i]) {
putchar(str[i]);
}
}
puts("");
return 0;
}