[一本通1751]字符串排序 题解

题目描述

给定一个由小写字母组成的字符串\(S\)。有\(m\)次操作,每次操作给定3个参数\(l\),\(r\),\(x\) 。如果\(x=1\),将\(Sl∼Sr\)升序排序;如果\(x=0\),将\(Sl∼Sr\)降序排序。你需要求出最终序列。

输入

第一行两个整数 \(n\),\(m\)。第二行一个字符串\(S\)。接下来\(m\)行每行三个整数\(x\),\(l\),\(r\)

输出

一行一个字符串表示答案。

输入样例

5 2 
cabcd 
1 3 1 
3 5 0

输出样例

abdcc

提示

数据规模

对于\(40%\)的数据,\(n,m≤1000\)
对于\(100%\)的数据,\(n,m≤100000\)

解题思路

本题的数据范围表示肯定不能每次操作都进行一排序(\(O(m\times nlog(n))\))。
鉴于这个是区间问题,我们可以想到线段树。再看由于字符只有26种,就可以想桶排序进行优化。每次排序的本质其实就是把第\(i\)个数移动到他该在的位置上面(相当于把他该在的区间修改为他的值)。于是我们开一个数组\(cnt_{u,i}\)表示线段树节点\(u\)上字符\(i\)出现次数。每次排序时候可以根据这个进行区间修改(同时还要维护这个数组)。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#define lc(x) ((x) << 1)
#define rc(x) (((x) << 1) | 1)
using namespace std;

const int N = 1e5 + 16;
const int M = N * 4;

int n, m;

int cnt[M][28], tag[M];

char str[N];

void Build(int u, int l, int r)
{
    if (l == r)
    {
        cnt[u][str[l] - 'a' + 1] = 1;
        return;
    }
    int mid = l + r >> 1;
    Build(lc(u), l, mid);
    Build(rc(u), mid + 1, r);
    for (int i = 1; i <= 26; i++) //Pushup
        cnt[u][i] = cnt[lc(u)][i] + cnt[rc(u)][i];
}

inline void SetTag(int u, int l, int r, int val)
{
    tag[u] = val;
    for (int i = 1; i <= 26; i++)
        cnt[u][i] = 0;
    cnt[u][val] = r - l + 1;
}

inline void Pushdown(int u, int l, int r)
{
    if (tag[u])
    {
        int mid = l + r >> 1;
        SetTag(lc(u), l, mid, tag[u]);
        SetTag(rc(u), mid + 1, r, tag[u]);
        tag[u] = 0;
    }
}

void Modify(int u, int l, int r, int x, int y, int val) //把x-y区间的值修改为val
{
    if (r < x || l > y)
        return;
    if (x <= l && r <= y)
    {
        SetTag(u, l, r, val);
        return;
    }
    Pushdown(u, l, r);
    int mid = l + r >> 1;
    Modify(lc(u), l, mid, x, y, val);
    Modify(rc(u), mid + 1, r, x, y, val);
    for (int i = 1; i <= 26; i++) //Pushup
        cnt[u][i] = cnt[lc(u)][i] + cnt[rc(u)][i];
}

int ret[28]; //承接Query的返回值

void Query(int u, int l, int r, int x, int y) //询问l-r之间的各字母出现频率
{
    if (r < x || l > y)
        return;
    if (x <= l && r <= y)
    {
        for (int i = 1; i <= 26; i++)
            ret[i] += cnt[u][i];
        return;
    }
    Pushdown(u, l, r);
    int mid = l + r >> 1;
    Query(lc(u), l, mid, x, y);
    Query(rc(u), mid + 1, r, x, y);
}

char AnsStr[N];

void PrintAns(int u, int l, int r)
{
    if (l == r)
    {
        for (int i = 1; i <= n; i++)
            if (cnt[u][i])
            {
                AnsStr[l] = i + 'a' - 1;
                break;
            }
        return;
    }
    Pushdown(u, l, r);
    int mid = l + r >> 1;
    PrintAns(lc(u), l, mid);
    PrintAns(rc(u), mid + 1, r);
}

int main()
{
    scanf(" %d %d", &n, &m);
    scanf(" %s", str + 1);
    Build(1, 1, n);
    while (m--)
    {
        int l, r, opt;
        scanf(" %d %d %d", &l, &r, &opt);
        memset(ret, 0, sizeof ret);
        Query(1, 1, n, l, r);
        if (opt == 1) //顺序排序
        {
            for (int i = 1; i <= 26; i++)
            {
                if (ret[i])
                {
                    Modify(1, 1, n, l, l + ret[i] - 1, i); //把i号字母放在前面
                    l += ret[i];                           //移动到i号字母之后
                }
            }
        }
        else
        {
            for (int i = 26; i >= 1; i--)
            {
                if (ret[i])
                {
                    Modify(1, 1, n, l, l + ret[i] - 1, i);
                    l += ret[i];
                }
            }
        }
    }
    PrintAns(1, 1, n);
    printf("%s", AnsStr + 1);
    return 0;
}
posted @ 2021-08-11 11:26  Icys  阅读(354)  评论(0编辑  收藏  举报