给定一个由小写字母组成的字符串\(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; }