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

題目描述

給定一個由小寫字母組成的字符串\(S\)。有\(m\)次操做,每次操做給定3個參數\(l\),\(r\),\(x\) 。若是\(x=1\),將\(Sl∼Sr\)升序排序;若是\(x=0\),將\(Sl∼Sr\)降序排序。你須要求出最終序列。ios

輸入

第一行兩個整數 \(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\)ui

解題思路

本題的數據範圍表示確定不能每次操做都進行一排序(\(O(m\times nlog(n))\))。
鑑於這個是區間問題,咱們能夠想到線段樹。再看因爲字符只有26種,就能夠想桶排序進行優化。每次排序的本質其實就是把第\(i\)個數移動到他該在的位置上面(至關於把他該在的區間修改成他的值)。因而咱們開一個數組\(cnt_{u,i}\)表示線段樹節點\(u\)上字符\(i\)出現次數。每次排序時候能夠根據這個進行區間修改(同時還要維護這個數組)。spa

代碼

#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;
}
相關文章
相關標籤/搜索