1. 程式人生 > >Luogu 2787 語文1(chin1)- 理理思維

Luogu 2787 語文1(chin1)- 理理思維

題目連結:傳送門

題目背景

蒟蒻HansBug在語文考場上,撓了無數次的頭,可腦子裡還是一片空白。

題目描述

考試開始了,可是蒟蒻HansBug腦中還是一片空白。哦不!準確的說是亂七八糟的。現在首要任務就是幫蒟蒻HansBug理理思維。假設HansBug的思維是一長串字串(字串中包含且僅包含26個字母),現在的你,有一張神奇的藥方,上面依次包含了三種操作:
獲取第x到第y個字元中字母k出現了多少次
將第x到第y個字元全部賦值為字母k
將第x到第y個字元按照A-Z的順序排序
你欣喜若狂之時,可是他腦細胞和RP已經因為之前過度緊張消耗殆盡,眼看試卷最後還有一篇800字的作文呢,所以這個關鍵的任務就交給你啦!

輸入格式:

第一行包含兩個整數N、M,分別表示HansBug的思維所包含的字母個數和藥方上操作個數。
第二行包含一個長度為N的字串,表示HansBug的思維。
第3-M+2行每行包含一條操作,三種操作格式如下:
操作1: 1 xi yi ki 表示將第xi到第yi個字元中ki出現的次數輸出
操作2: 2 xi yi ki 表示將第xi到第yi個字元全部替換為ki
操作3: 3 xi yi 表示將第xi到第yi個字元按照A-Z的順序排序

輸出格式:

輸出為若干行,每行包含一個整數,依次為所有操作1所得的結果。

輸入樣例

10 5
ABCDABCDCD
1 1 3 A
3 1 5
1 1 3 A
2 1 2 B
1 2 3 B

輸出樣例

1
2
2

說明

樣例說明:
在這裡插入圖片描述
資料規模:
在這裡插入圖片描述
此題目中大小寫不敏感。

這也是珂朵莉樹的好題
正常珂朵莉樹開O2之後跑到400ms左右是沒問題的
空間複雜度也在4MB左右
線段樹的話2000ms和幾十MB是很普遍的
優勢又體現出來了(~ ̄▽ ̄)~
具體怎麼寫在程式碼裡說

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <algorithm>
#include
<climits>
#include <queue> #include <map> #include <set> #include <vector> #include <iomanip> #define A 50010 #define B 2010 #define ll long long #define iter set<node>::iterator using namespace std; struct node { int l, r; mutable char v; node (int L, int R = -1, char V = -1) : l(L), r(R), v(V){} bool operator < (const node &a) const{ return l < a.l; } }; set<node> s; int n, m, cnt[27]; int opt, x, y; iter split(int pos) { iter it = s.lower_bound(node(pos)); if (it != s.end() and it->l == pos) return it; it--; int lcc = it->l, rcc = it->r; char val = it->v; s.erase(it); s.insert(node(lcc, pos - 1, val)); return s.insert(node(pos, rcc, val)).first; } void assign(int l, int r, char val) { iter rcc = split(r + 1), lcc = split(l); s.erase(lcc, rcc); s.insert(node(l, r, val)); } int asksum(int l, int r, char ds) { //查詢區間內指定字母的個數 iter rcc = split(r + 1), lcc = split(l); int ans = 0; for (; lcc != rcc; lcc++) if (lcc->v == ds) ans += lcc->r - lcc->l + 1; return ans; } void sor(int l, int r) { iter rcc = split(r + 1), lcc = split(l); //記下每個字母有多少個 for (iter it = lcc; it != rcc; it++) cnt[it->v - 'A'] += it->r - it->l + 1; s.erase(lcc, rcc); int pos = l; for (int i = 0; i < 26; i++) if (cnt[i]) { s.insert(node(pos, pos + cnt[i] - 1, i + 'A')); //按順序插進去 pos += cnt[i]; //移位,繼續往後放 cnt[i] = 0; //放到最後清也可以 } } char ss[A], c[2]; int main() { scanf("%d%d", &n, &m); scanf("%s", ss + 1); for (int i = 1; i <= n; i++) ss[i] = toupper(ss[i]); //記得都轉成大寫的 for (int i = 1; i <= n; i++) s.insert(node(i, i, ss[i])); //初始化的字母都塞進去 while (m--) { scanf("%d%d%d", &opt, &x, &y); if (opt != 3) scanf("%s", c + 1), c[1] = toupper(c[1]); if (opt == 1) printf("%d\n", asksum(x, y, c[1])); if (opt == 2) assign(x, y, c[1]); if (opt == 3) sor(x, y); } return 0; }