题意:
给你n个区间操作,把每个区间内的小写英文字母以非递增或非递减的方式进行排序,最后输出结果。
思路:
参考自:http://www.cnblogs.com/crazyacking/p/4649878.html
因为只有26个字母,所以建立26颗线段树,来保存每个字母在所有区间的位置情况。
线段树的作用:快速查询区间某个字符的个数,快速更新区间字符的个数。
具体看代码内注释。
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
const int M = 30;
typedef long long LL;
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
#define start 1, n, 1
int n, q;
int cnt[M];
char ch[N];
struct ST {
int lazy[N<<2], num[N<<2];
void init() {
memset(lazy, -1, sizeof(lazy));
memset(num, 0, sizeof(num));
}
void pushDown(int l, int r, int rt) {
int mid = (l+r)>>1;
if(lazy[rt] == 0) { //lazy为0,下面都不存在字母。
//lazy为1,下面区间都存在字符。
num[rt<<1] = num[rt<<1|1] = 0;
lazy[rt<<1] = lazy[rt<<1|1] = 0;
}
else {
num[rt<<1] = mid-l+1;
num[rt<<1|1] = r-mid;
lazy[rt<<1] = lazy[rt<<1|1] = 1;
}
lazy[rt] = -1;//-1:没有需要往下更新的
}
void pushUp(int l, int r, int rt) {
num[rt] = num[rt<<1] + num[rt<<1|1];
}
void get(int l, int r, int rt, char c) {
if(l == r) {
ch[l] = c;
return;
}
if(lazy[rt] != -1) pushDown(l, r, rt);
int mid = (l+r)>>1;
if(num[rt<<1] != 0) get(lson, c);
if(num[rt<<1|1] != 0) get(rson, c);
pushUp(l, r, rt);
}
int query(int l, int r, int rt, int x, int y) {
if(x <= l && y >= r) {
int ret = num[rt];
num[rt] = 0;//把所有字符取出来,因此设为0
lazy[rt] = 0;//下面的区间都为0,因此有个延迟标记
return ret;
}
if(lazy[rt] != -1) pushDown(l, r, rt);
int ret = 0;
int mid = (l+r)>>1;
if(x <= mid && num[rt<<1] != 0) ret += query(lson, x, y);
if(y > mid && num[rt<<1|1] != 0) ret += query(rson, x, y);
pushUp(l, r, rt);
return ret;
}
void update(int l, int r, int rt, int x, int y) {
if(x <= l && y >= r) {
num[rt] = (r-l+1);
lazy[rt] = 1;
return;
}
if(lazy[rt] != -1) pushDown(l, r, rt);
int mid = (l+r)>>1;
if(x <= mid) update(lson, x, y);
if(y > mid) update(rson, x, y);
pushUp(l, r, rt);
}
}st[26];
int main() {
scanf("%d%d", &n, &q);
scanf("%s", ch+1);
for(int i = 0;i < 26; i++) st[i].init();
for(int i = 1;i <= n; i++) {
int idx = ch[i]-'a';
st[idx].update(start, i, i); //先初始化线段树,把每个字符在区间的情况搞出来。
}
for(int i = 0;i < q; i++) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
for(int j = 0;j < 26; j++)
cnt[j] = st[j].query(start, l, r);//统计区间内,每个字符的个数。在询问完后,这个区间内的字符个数为0,相当于已经把所有字符取出来了。
if(k) {
int tl = l;
//非递减,就把字符从小到大填到l~r区间
for(int j = 0;j < 26; j++) {
if(cnt[j] == 0) continue;
st[j].update(start, tl, tl+cnt[j]-1);
tl = tl+cnt[j];
}
}
else {
int tl = l;
//非递增,字符从大到校填充l~r区间
for(int j = 25;j >= 0; j--) {
if(cnt[j] == 0) continue;
st[j].update(start, tl, tl+cnt[j]-1);
tl = tl+cnt[j];
}
}
}
//get求出每个位置的字符情况。
for(int i = 0;i < 26; i++) st[i].get(start, 'a'+i);
puts(ch+1);
return 0;
}