校赛F 比比谁更快(线段树)

http://acm.cug.edu.cn/JudgeOnline/problem.php?cid=1153&pid=5
校赛F 比比谁更快(线段树)_第1张图片

题意:给你一个字符串,各两个操作:
ch=0,[l,r]降序
ch=1,[l,r]升序

如果是newer的话,应该会想用暴力,直接对区间sort,但是很明显是超时的(校赛怎么会这么简单呢!)
很容易想到线段数,对于线段数,我们可以先理清一下知识点,线段树大概有三种操作:
- 单点更新
- 区间更新
- RMQ

显然这里要用区间更新的操作。
区间更新有三大操作:

build()建树
update()更新
query()求和
sum[4*N]存储线段数节点

但是一般的区间更新的线段数是区间加或者区间减等统一的操作,所以对于区间排序需要对区间查询模板更改一下。

sum[4*N][26]来存储线段数节点
setv[4*N]来存储当前区间覆盖的字母
cnt[26]表示每个字母在当前区间出现的次数

下面以样例“abacdabcda”为例进行阐述:
首先是建树过程:
这一部分计算出sum[4N][26]和setv[4N]的值

其次,对于每一个查询,“l,r,ch”
都要用query(int rt,int L,int R,int l,int r,int i)计算[l,r]区间cnt[i]的值。

最后一步,对于每一个查询”l,r,ch”
根据ch的值:
做update(int rt,int L,int R,int l,int r,int i)
如果ch=0,对[l,r]降序
做法是:对cnt[i]数组从25->0 遍历,若cnt[i]>0,表示这个字符存在,且个数为cnt[i]个
所以要把[l,l+cnt[i]]个位置都要修改为cnt[i]对应的字母,并把l(当前位置后移cnt[i]位)

如果ch=1,对[l,r]升序
做法相同,只是从[0->25]遍历。

#include 
#include 
#include 
#include 
#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;
}

你可能感兴趣的:(校赛F 比比谁更快(线段树))