传送门
题意: 给定一个长度不超过10^5的字符串(小写英文字母),和不超过50000个操作。每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序。最后输出最终的字符串。
思路: 注意到只有26个字母,而且长度不超过1e5,所以可以来一个计数排序,建立26个线段树,每个字母用一个线段树维护。
有一个坑点就是区间赋0的时候,主要lzay标记不用时应置为-1,不能是0,这一点坑了我好久。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n, q;
char s[N];
struct sgt {
int t[N << 2], lz[N << 2];
void pushup(int rt) {
t[rt] = t[rt << 1] + t[rt << 1 | 1];
}
void pushdown(int rt, int ln, int rn) {
if (lz[rt] != -1) {
t[rt << 1] = lz[rt] * ln;
t[rt << 1 | 1] = lz[rt] * rn;
lz[rt << 1] = lz[rt];
lz[rt << 1 | 1] = lz[rt];
lz[rt] = -1;
}
}
void update(int l, int r, int rt, int v, int L, int R) {
if (L <= l && r <= R) {
t[rt] = (r - l + 1) * v;
lz[rt] = v;
return;
}
int m = l + r >> 1;
pushdown(rt, m - l + 1, r - m);
if (m >= L)
update(l, m, rt << 1, v, L, R);
if (m < R)
update(m + 1, r, rt << 1 | 1, v, L, R);
pushup(rt);
}
int query(int l, int r, int rt, int L, int R) {
if (L <= l && r <= R)
return t[rt];
int m = l + r >> 1, ans = 0;
pushdown(rt, m - l + 1, r - m);
if (m >= L)
ans += query(l, m, rt << 1, L, R);
if (m < R)
ans += query(m + 1, r, rt << 1 | 1, L, R);
pushup(rt);
return ans;
}
} T[26];
int cnt[26];
int main() {
scanf("%d%d%s", &n, &q, s + 1);
for (int i = 1; i <= n; i++)
T[s[i] - 'a'].update(1, n, 1, 1, i, i);
for (int l, r, k, tt = 1; tt <= q; tt++) {
scanf("%d%d%d", &l, &r, &k);
if (k == 1) {
for (int i = 0; i < 26; i++)
cnt[i] = T[i].query(1, n, 1, l, r);
for (int i = 0; i < 26; i++)
T[i].update(1, n, 1, 0, l, r);
int a = l;
for (int i = 0; i < 26; i++)
if (cnt[i] != 0)
T[i].update(1, n, 1, 1, a, a + cnt[i] - 1), a += cnt[i];
} else {
for (int i = 0; i < 26; i++)
cnt[i] = T[i].query(1, n, 1, l, r);
for (int i = 0; i < 26; i++)
T[i].update(1, n, 1, 0, l, r);
int a = l;
for (int i = 25; i >= 0; i--)
if (cnt[i] != 0)
T[i].update(1, n, 1, 1, a, a + cnt[i] - 1), a += cnt[i];
}
}
for (int i = 1; i <= n; i++)
for (int j = 0; j < 26; j++)
if (T[j].query(1, n, 1, i, i))
printf("%c", j + 'a');
return 0;
}