[线段树] codeforces 558E. A Simple Task

题意:
给一个长度n的字符串,q次操作,每次操作把[l,r]排序,k=0非递增,k=1非递减。
题解:
采用计数排序的复杂度是 O(nq) ,无法通过,但有所启示。
可以看出计数就是区间求和,排序就是区间更新,可以用线段树维护。
做法是建立26棵线段树,第i棵树维护第i个字母的位置信息。
计数时,在26棵线段树内分别做一次查询,排序时根据递增还是递减,把相应的区间赋值为相应的字母。

#include<bits/stdc++.h>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int N = 1e5+5;
char s[N];
struct SegTree{
    int tree[N<<2], lazy[N<<2];
    void push_up(int rt){ tree[rt] = tree[rt<<1] + tree[rt<<1|1]; }
    void push_down(int rt, int l, int r){
        if(lazy[rt] != -1){
            int mid = (l+r) >> 1;
            tree[rt<<1] = (mid-l+1)*lazy[rt];
            tree[rt<<1|1] = (r-mid)*lazy[rt];
            lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
            lazy[rt] = -1;
        }
    }
    void build(int rt, int l, int r, char c){
        tree[rt] = 0, lazy[rt] = -1;
        if(l == r){
            tree[rt] = (s[l] == c);
            return;
        }
        int mid = (l+r) >> 1;
        build(lson, c);
        build(rson, c);
        push_up(rt);
    }
    void update(int rt, int l, int r, int ql, int qr, int v){
        if(ql <= l && qr >= r){
            tree[rt] = (r-l+1)*v;
            lazy[rt] = v;
            return;
        }
        push_down(rt, l, r);
        int mid = (l+r) >> 1;
        if(ql <= mid) update(lson, ql, qr, v);
        if(qr > mid) update(rson, ql, qr, v);
        push_up(rt);
    }
    int query(int rt, int l, int r, int ql, int qr){
        if(ql <= l && qr >= r) return tree[rt];
        push_down(rt, l, r);
        int res = 0, mid = (l+r) >> 1;
        if(ql <= mid) res += query(lson, ql, qr);
        if(qr > mid) res += query(rson, ql, qr);
        push_up(rt);
        return res;
    }
    void print(int rt, int l, int r, char c){
        if(l == r){
            if(tree[rt]) s[l] = c;
            return;
        }
        push_down(rt, l, r);
        int mid = (l+r) >> 1;
        if(tree[rt]) print(lson, c), print(rson,c);
        push_up(rt);
    }
}tr[30];
void print(int n){
    for(int i = 0; i < 26; ++i) tr[i].print(1, 1, n, i+'a');
    s[n+1] = 0;
    puts(s+1);
}
int main(){
    int n, q;
    scanf("%d%d", &n, &q);
    scanf("%s", s+1);
    for(int i = 0; i < 26; ++i) tr[i].build(1, 1, n, 'a'+i);
    while(q--){
        int l, r, k;
        scanf("%d%d%d", &l, &r, &k);
        int cot[30] = {0};
        for(int i = 0; i < 26; ++i) cot[i] = tr[i].query(1, 1, n, l, r);
        for(int i = 0; i < 26; ++i) tr[i].update(1, 1, n, l, r, 0);
        if(k == 0){
            for(int i = 25; i >= 0; --i){
                if(!cot[i]) continue;
                tr[i].update(1, 1, n, l, l+cot[i]-1, 1);
                l += cot[i];
            }
        }
        else{
            for(int i = 0; i <= 25; ++i){
                if(!cot[i]) continue;
                tr[i].update(1, 1, n, l, l+cot[i]-1, 1);
                l += cot[i];
            }
        }
    }
    print(n);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值