字符串排序
题目链接:ybt高效进阶4-4-5
题目大意
给你一个字符串,然后有一些操作,每次把一个区间的子串按升序排序或降序排序。
要你输出最后的字符串。
思路
我们考虑把排序的过程分解。
我们考虑先找到每个字符在这个区间中出现了多少次,然后把它们放在一起,然后再按照要求的顺序把字符放回去,因为是升序或降序排序,所以同理一个字符肯定是捆在一起的,那就相当于区间求值和区间修改。
那我们可以想到用 26 26 26 个树状数组分别记录每个字符的出现情况。
然后注意每次是更改,而不是相加。要把之前的记录覆盖掉的那种。
代码
#include<cstdio>
#include<cstring>
using namespace std;
struct Tree {
int x;
}tree[27][800001];
int n, m, x, y, op;
int tmp[27], f[800001];
char c[100001];
void up_all(int now) {
//上浮值
for (int i = 0; i < 26; i++)//一定要全部都枚举
tree[i][now].x = tree[i][now << 1].x + tree[i][now << 1 | 1].x;
}
void Add(int now, int l, int r, int pl) {
//更改这个位置的归属
f[now] = pl;
for (int i = 0; i < 26; i++) tree[i][now].x = 0;
tree[pl][now].x = r - l + 1;
}
void down(int now, int l, int r) {
//下传标记
if (f[now] == -1) return ;//没有标记
if (l != r) {
int mid = (l + r) >> 1;
Add(now << 1, l, mid, f[now]);
Add(now << 1 | 1, mid + 1, r, f[now]);
}
f[now] = -1;
}
void build(int now, int l, int r) {
//初始
if (l == r) {
tree[c[l] - 'a'][now].x = 1;
return ;
}
int mid = (l + r) >> 1;
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
up_all(now);
}
int get_sum(int now, int l, int r, int L, int R, int pl) {
//求这个区间中有多少个某个字符
if (L <= l && r <= R) {
return tree[pl][now].x;
}
down(now, l, r);
int mid = (l + r) >> 1;
int re = 0;
if (L <= mid) re += get_sum(now << 1, l, mid, L, R, pl);
if (mid + 1 <= R) re += get_sum(now << 1 | 1, mid + 1, r, L, R, pl);
return re;
}
void add_num(int now, int l, int r, int L, int R, int pl) {
//更改值,把一个区间的字符都修改乘这个
if (L > R) return ;
if (L <= l && r <= R) {
Add(now, l, r, pl);
return ;
}
down(now, l, r);
int mid = (l + r) >> 1;
if (L <= mid) add_num(now << 1, l, mid, L, R, pl);
if (mid + 1 <= R) add_num(now << 1 | 1, mid + 1, r, L, R, pl);
up_all(now);
}
void work_up(int x, int y) {
//升序排序
for (int i = 0; i < 26; i++) {
//先找出每个的个数
tmp[i] = get_sum(1, 1, n, x, y, i);
}
int now = x;
for (int i = 0; i < 26; i++) {
//然后再按顺序放进去
add_num(1, 1, n, now, now + tmp[i] - 1, i);
now += tmp[i];
if (now > y) return ;
}
}
void work_down(int x, int y) {
//将序排序跟升序排序同理,只是按的顺序反了过来
for (int i = 0; i < 26; i++) {
tmp[i] = get_sum(1, 1, n, x, y, i);
}
int now = x;
for (int i = 25; i >= 0; i--) {
add_num(1, 1, n, now, now + tmp[i] - 1, i);
now += tmp[i];
if (now > y) return ;
}
}
void write(int now, int l, int r) {
//输出
if (l == r) {
for (int i = 0; i < 26; i++)//找它是哪个字符
if (tree[i][now].x) {
printf("%c", 'a' + i);
return ;
}
}
down(now, l, r);
int mid = (l + r) >> 1;
write(now << 1, l, mid);
write(now << 1 | 1, mid + 1, r);
}
int main() {
memset(f, -1, sizeof(f));
scanf("%d %d", &n, &m);
scanf("%s", c + 1);
build(1, 1, n);
while (m--) {
scanf("%d %d %d", &x, &y, &op);
if (op == 1) {
work_up(x, y);
}
else {
work_down(x, y);
}
}
write(1, 1, n);
return 0;
}