【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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值