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

题意:

给你n个区间操作,把每个区间内的小写英文字母以非递增或非递减的方式进行排序,最后输出结果。

思路:

参考自:http://www.cnblogs.com/crazyacking/p/4649878.html

因为只有26个字母,所以建立26颗线段树,来保存每个字母在所有区间的位置情况。

线段树的作用:快速查询区间某个字符的个数,快速更新区间字符的个数。

具体看代码内注释。


code:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+5;
const int M = 30;
typedef long long LL;

#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
#define start 1, n, 1

int n, q;
int cnt[M];
char ch[N];

struct ST {
    int lazy[N<<2], num[N<<2];
    void init() {
        memset(lazy, -1, sizeof(lazy));
        memset(num, 0, sizeof(num));
    }
    void pushDown(int l, int r, int rt) {
        int mid = (l+r)>>1;
        if(lazy[rt] == 0) { //lazy为0,下面都不存在字母。
            //lazy为1,下面区间都存在字符。
            num[rt<<1] = num[rt<<1|1] = 0;
            lazy[rt<<1] = lazy[rt<<1|1] = 0;
        }
        else {
            num[rt<<1] = mid-l+1;
            num[rt<<1|1] = r-mid;
            lazy[rt<<1] = lazy[rt<<1|1] = 1;
        }
        lazy[rt] = -1;//-1:没有需要往下更新的
    }
    void pushUp(int l, int r, int rt) {
        num[rt] = num[rt<<1] + num[rt<<1|1];
    }
    void get(int l, int r, int rt, char c) {
        if(l == r) {
            ch[l] = c;
            return; 
        }
        if(lazy[rt] != -1) pushDown(l, r, rt);
        int mid = (l+r)>>1;
        if(num[rt<<1] != 0) get(lson, c);
        if(num[rt<<1|1] != 0) get(rson, c);
        pushUp(l, r, rt);
    }
        
    int query(int l, int r, int rt, int x, int y) {
        if(x <= l && y >= r) {
            int ret = num[rt];
            num[rt] = 0;//把所有字符取出来,因此设为0
            lazy[rt] = 0;//下面的区间都为0,因此有个延迟标记
            return ret;
        }
        if(lazy[rt] != -1) pushDown(l, r, rt);
        int ret = 0;
        int mid = (l+r)>>1;
        if(x <= mid && num[rt<<1] != 0) ret += query(lson, x, y);
        if(y > mid && num[rt<<1|1] != 0) ret += query(rson, x, y);
        pushUp(l, r, rt);
        return ret;
    }
    void update(int l, int r, int rt, int x, int y) {
        if(x <= l && y >= r) {
            num[rt] = (r-l+1);
            lazy[rt] = 1;
            return;
        }
        if(lazy[rt] != -1) pushDown(l, r, rt);
        int mid = (l+r)>>1;
        if(x <= mid) update(lson, x, y);
        if(y > mid) update(rson, x, y);
        pushUp(l, r, rt);
    }
}st[26];

int main() {
    scanf("%d%d", &n, &q);
    scanf("%s", ch+1);

    for(int i = 0;i < 26; i++) st[i].init();
    for(int i = 1;i <= n; i++) {
        int idx = ch[i]-'a';
        st[idx].update(start, i, i);    //先初始化线段树,把每个字符在区间的情况搞出来。
    }

    for(int i = 0;i < q; i++) {
        int l, r, k;
        scanf("%d%d%d", &l, &r, &k);
        for(int j = 0;j < 26; j++)
            cnt[j] = st[j].query(start, l, r);//统计区间内,每个字符的个数。在询问完后,这个区间内的字符个数为0,相当于已经把所有字符取出来了。
        if(k) {
            int tl = l;
            //非递减,就把字符从小到大填到l~r区间
            for(int j = 0;j < 26; j++) {
                if(cnt[j] == 0) continue;
                st[j].update(start, tl, tl+cnt[j]-1);
                tl = tl+cnt[j];
            }
        }
        else {
            int tl = l;
            //非递增,字符从大到校填充l~r区间
            for(int j = 25;j >= 0; j--) {
                if(cnt[j] == 0) continue;
                st[j].update(start, tl, tl+cnt[j]-1);
                tl = tl+cnt[j];
            }
        }
    }
    //get求出每个位置的字符情况。
    for(int i = 0;i < 26; i++) st[i].get(start, 'a'+i);
    puts(ch+1);
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值