题目
题目描述
Petya has a string of length nn consisting of small and large English letters and digits.
He performs mm operations. Each operation is described with two integers ll and rr and a character cc : Petya removes from the string all characters cc on positions between ll and rr , inclusive. It’s obvious that the length of the string remains the same or decreases after each operation.
Find how the string will look like after Petya performs all mm operations.
输入格式
The first string contains two integers nn and mm ( 1<=n,m<=2·10^{5}1<=n,m<=2⋅10
5
) — the length of the string and the number of operations.
The second line contains the string of length nn , consisting of small and large English letters and digits. Positions in the string are enumerated from 11 .
Each of the next mm lines contains two integers ll and rr ( 1<=l<=r1<=l<=r ), followed by a character cc , which is a small or large English letter or a digit. This line describes one operation. It is guaranteed that rr doesn’t exceed the length of the string ss before current operation.
输出格式
Print the string Petya will obtain after performing all mm operations. If the strings becomes empty after all operations, print an empty line.
题意翻译
长度为n的串,m次操作,删除一段区间内的所有为c的字符,输出删除完毕后的字符串。
输入输出样例
输入 #1复制
4 2
abac
1 3 a
2 2 c
输出 #1复制
b
输入 #2复制
3 2
A0z
1 3 0
1 1 z
输出 #2复制
Az
输入 #3复制
10 4
agtFrgF4aF
2 5 g
4 9 F
1 5 4
1 7 a
输出 #3复制
tFrg4
输入 #4复制
9 5
aAAaBBccD
1 4 a
5 6 c
2 3 B
4 4 D
2 3 A
输出 #4复制
AB
说明/提示
In the first example during the first operation both letters ‘a’ are removed, so the string becomes “bc”. During the second operation the letter ‘c’ (on the second position) is removed, and the string becomes “b”.
In the second example during the first operation Petya removes ‘0’ from the second position. After that the string becomes “Az”. During the second operations the string doesn’t change.
思路
首先我们发现,这题只有删除操作。
那其实我们的重点完全不在如何维护删除,因为每个点最多被删一次,我们完全可以暴力单点删除,不需要多余的技巧去维护,只要单点删除复杂度是对的,均摊下来复杂度就是对的。
那么我们就把重心放在如何找出该被删的数。
有一个很暴力的想法是直接去存每一个字符的所有出现位置,那我们用 set 维护,就可以做到均摊一个 \loglog。
但是这题的下标是会有变化的,我们需要根据给出的下标找出原序列下标。我们可以把所有未被删除的点塞进一个平衡树里,然后就只需要支持第 kk 大查询即可。这题权值线段树比任何一种平衡树都好写很多,所以我使用权值线段树。
这样这题就做完了,总时间复杂度 O(nlogn)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int get(char c)
{
if(c>='0'&&c<='9')
return c-'0';
if(c>='A'&&c<='Z')
return c-'A'+10;
return c-'a'+36;
}
set<int>d[62];
int bit[N],del[N];
void ins(int x)
{
for(; x<N; x+=x&-x) bit[x]++;
}
int qq(int x)
{
int res=0;
for(; x; x-=x&-x)
res+=bit[x];
return res;
}
int get_pos(int x)
{
int ll=x,rr=N-1;
while(ll<rr)
{
int mm=(ll+rr)/2;
if(mm-qq(mm)>=x)
rr=mm;
else
ll=mm+1;
}
return ll;
}
char s[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
scanf("%s",s);
for(int i=0; i<n; i++) d[get(s[i])].insert(i+1);
for(int cs=0; cs<m; cs++)
{
int x,y;
scanf("%d%d",&x,&y);
char c[4];
scanf("%s",c);
int id=get(c[0]);
x=get_pos(x);
y=get_pos(y);
auto it=d[id].lower_bound(x);
auto it2=it;
while(it2!=d[id].end()&&*it2<=y)
{
del[*it2]=1;
ins(*it2);
it2++;
}
d[id].erase(it,it2);
}
for(int i=1; i<=n; i++) if(!del[i]) putchar(s[i-1]);
}