【ybt高效进阶4-4-5】字符串排序

字符串排序

题目链接: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;
}

猜你喜欢

转载自blog.csdn.net/weixin_43346722/article/details/115336217