cf558E. A Simple Task(权值线段树+计数排序)

传送门
题意: 给定一个长度不超过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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值