CF558E A Simple Task(线段树)

题目

题目大意: 给定一个长度不超过 1 0 5 10^5 105的字符串(小写英文字母),和不超过 50000 50000 50000个操作。

每个操作 L   R   K L\ R\ K L R K 表示给区间 [ L , R ] [L,R] [L,R]的字符串排序, K = 1 K=1 K=1为升序, K = 0 K=0 K=0为降序。

最后输出最终的字符串。

题解

  • 刚看题的时候一点思路都没有只会暴力sort,后来根据复杂度开始 y y yy yy算法,然后想到了线段树 q w q qwq qwq
  • 由于字母个数只有26个,我们可以对每个字母建一棵权值线段树,记录每个字母在区间内的出现次数, 对与升序,正序循环区间覆盖即可。

c o d e code code

#include <bits/stdc++.h> 
using namespace std; 
const int N = 1e5 + 100; 
const int M = 4e5 + 100; 

template <class T>
inline void read(T &s) {
	s = 0; T w = 1, ch = getchar(); 
	while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
	while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
	s *= w; 	
}

int n, q; 
char ch[N]; 
int add[30][M], sum[30][M]; 

inline void push_up(int p, int id) {
	sum[id][p] = sum[id][p<<1] + sum[id][p<<1|1]; 
	return ; 
}

void build(int p, int l, int r) {
	if (l == r) {
		int id = ch[l] - 'a' + 1; 
		sum[id][p] = 1; 
		return ; 
	}
	int mid = (l + r) >> 1; 
	build(p<<1, l, mid); 
	build(p<<1|1, mid + 1, r); 
	for (int i = 1; i <= 26; ++i) push_up(p, i); 
}

inline void push_down(int p, int l, int r, int id) {
	if (add[id][p] != -1) {
		int mid = (l + r) >> 1; 
		sum[id][p<<1] = add[id][p] * (mid - l + 1); 
		sum[id][p<<1|1] = add[id][p] * (r - mid); 
		add[id][p<<1] = add[id][p]; 
		add[id][p<<1|1] = add[id][p]; 
		add[id][p] = -1; 
	}
}

void update(int p, int l, int r, int ql, int qr, int w, int id) {
	if (ql <= l && qr >= r) {
		sum[id][p] = w * (r - l + 1); 
		add[id][p] = w; 
		return ; 
	}
	push_down(p, l, r, id); 
	int mid = (l + r) >> 1; 
	if (ql <= mid) update(p<<1, l, mid, ql, qr, w, id); 
	if (qr > mid) update(p<<1|1, mid + 1, r, ql, qr, w, id); 
	push_up(p, id); 
}

int query(int p, int l, int r, int ql, int qr, int id) {
	if (ql <= l && qr >= r) return sum[id][p]; 
	push_down(p, l, r, id); 
	int mid = (l + r) >> 1; 
	int val = 0; 
	if (ql <= mid) val += query(p<<1, l, mid, ql, qr, id); 
	if (qr > mid) val += query(p<<1|1, mid + 1, r, ql, qr, id); 
	return val; 
}

void output(int p, int l, int r) {
	if (l == r) {
		for (int i = 1; i <= 26; ++i) {
			if (sum[i][p]) {
				ch[l] = (char)(i + 'a' - 1); 
				break; 
			}
		}
		return ; 
	}
	for (int i = 1; i <= 26; ++i) 
		push_down(p, l, r, i); 
	int mid = (l + r) >> 1; 
	output(p<<1, l, mid); 
	output(p<<1|1, mid + 1, r); 
}

int main() {
	read(n), read(q); 
	scanf("%s", ch + 1); 
	memset(add, -1, sizeof(add)); 

	build(1, 1, n); 

	while (q--) { 
		int l, r, opt; 
		read(l), read(r), read(opt); 
		if (opt) { // 升序
			int tmp = l; 
			for (int i = 1; i <= 26; ++i) {
				int cnt = query(1, 1, n, l, r, i); 
				if (!cnt) continue; 
				update(1, 1, n, l, r, 0, i); 
				update(1, 1, n, tmp, tmp + cnt - 1, 1, i); 
				tmp += cnt; 
			}
		}
		else { // 降序
			int tmp = l; 
			for (int i = 26; i >= 1; --i) {
				int cnt = query(1, 1, n, l, r, i); 
				if (!cnt) continue; 
				update(1, 1, n, l, r, 0, i); 
				update(1, 1, n, tmp, tmp + cnt - 1, 1, i); 
				tmp += cnt; 
			}
		}
	}

	output(1, 1, n); 
	for (int i = 1; i <= n; ++i) 
		putchar(ch[i]); 
	return 0;  
}

/*
4 1
abcd 
1 4 1
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值