Codeforces 558E A Simple Task(线段树区间更新)

题意:

给定一个长度为n的字符串,有q次操作,每次操作将一个区间内的字符按升序或降序排列,输出q次操作后的字符串,串中只包含小写英文字母.

解析:

考虑到字符集大小只有26,联想到计数排序,对一个区间做一次排序相当于对区间内每一种字符分别统计一遍个数后,再按照字符的顺序(升序或降序)依次重新填进区间内,可以用26棵权值线段树分别维护每一种字符的位置,复杂度 O(26nlog2(n)+52qlog2(n))

my code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls,L,M
#define rson rs,M+1,R
using namespace std;

typedef long long ll;
const int N = 100005;
const int sigma = 26;

int sumv[N<<2][sigma], setv[N<<2]; //setv表示当前区间被覆盖的字母
int cnt[26]; //每个坐标在当前区间出现的次数
char str[N];

void pushUp(int o) {
    for(int i = 0; i < sigma; i++) {
        sumv[o][i] = sumv[ls][i] + sumv[rs][i];
    }
}

void initNode(int o) {
    memset(sumv[o], 0, sizeof(sumv[o]));
}

void pushDown(int o, int L, int R) {
    int M = (L+R)/2;
    if(setv[o] != -1) {
        setv[ls] = setv[rs] = setv[o];
        initNode(ls);
        sumv[ls][setv[o]] = (M-L+1);
        initNode(rs);
        sumv[rs][setv[o]] = (R-M);
        setv[o] = -1;
    }
}
void build(int o, int L, int R) {
    if(L == R) {
        initNode(o);
        sumv[o][str[L]-'a'] = 1;
        setv[o] = str[L]-'a'; 
        return ;
    }
    setv[o] = -1;
    int M = (L+R)/2;
    build(lson);
    build(rson);
    pushUp(o);
}

int query(int o, int L, int R, int ql, int qr, int val) {
    if(ql <= L && R <= qr)
        return sumv[o][val];
    pushDown(o, L, R);
    int M = (L+R)/2, ret = 0;
    if(ql <= M) ret += query(lson, ql, qr, val);
    if(qr > M) ret += query(rson, ql, qr, val);
    return ret;
}

void modify(int o, int L, int R, int ql, int qr, int val) {
    if(ql <= L && R <= qr) {
        for(int i = 0; i < sigma; i++)
            sumv[o][i] = 0;
        setv[o] = val;
        sumv[o][setv[o]] = (R-L+1);
        return ;
    }
    pushDown(o, L, R);
    int M = (L+R)/2;
    if(ql <= M) modify(lson, ql, qr, val);
    if(qr > M) modify(rson, ql, qr, val);
    pushUp(o);
}

void getStr(int o, int L, int R) {
    if(L == R) {
        printf("%c", (char)('a'+setv[o]));
        return ;
    }
    pushDown(o, L, R);
    int M = (L+R)/2;
    getStr(lson);
    getStr(rson);
}

int n, q;
int main() {
    while(scanf("%d%d", &n, &q) != EOF) {
        scanf("%s", str+1);
        build(1, 1, n);

        int ql, qr, ch;
        while(q--) {
            scanf("%d%d%d", &ql, &qr, &ch);
            for(int i = 0; i < sigma; i++)
                cnt[i] = query(1, 1, n, ql, qr, i);

            int pos = ql;
            if(ch == 1) {
                for(int i = 0; i < sigma; i++) {
                    if(cnt[i] > 0)
                        modify(1, 1, n, pos, pos+cnt[i]-1, i);
                    pos += cnt[i];
                }
            }else {
                for(int i = sigma-1; i >= 0; i--) {
                    if(cnt[i] > 0)
                        modify(1, 1, n, pos, pos+cnt[i]-1, i);
                    pos += cnt[i];
                }
            }
        }
        getStr(1, 1, n); puts("");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值