1. 程式人生 > 實用技巧 >java23種設計模式——六、介面卡模式

java23種設計模式——六、介面卡模式

題目描述:

給定一個由小寫字母組成的字串$s$。

有$m$次操作,每次操作給定$3$個引數$l,r,x$。

如果$x$ $=$ $1$,將$s[l]$ $~$ $s[r]$升序排序;

如果$x$ $=$ $0$,將$s[l]$ $~$ $s[r]$降序排序。

你需要求出最終序列。

輸入格式:

第一行兩個整數$n,m$。

第二行一個字串$s$。

接下來$m$行每行三個整數$x,l,r$。

輸出格式:

一行一個字串表示答案。

輸入樣例:

5 2
cabcd
1 3 1
3 5 0

輸出樣例:

abdcc

資料範圍:

對於$100\%$的資料,$n,m$ $≤$ $100000$。




思路:

因為資料達到了$1e5$,所以考慮$m$ $log$ $n$的複雜度(可能有常數)。

觀察到字串只由小寫字母組成,所以考慮一種類似於桶排的思想。

首先考慮一個$01$序列,如果將它排序只需要考慮$0$的數量和$1$的數量即可。

如果把字串看成一個由$1$ $~$ $26$構成的序列,並現將$1$看作$0$,其餘看作$1$,將這個字串排序就會變成一個前面是$0$,後面是$1$的序列,因為是把$1$轉化成$0$的,所以序列中的$0$就是$1$;再將$1$和$2$看作$0$,其餘看作$1$,再排序就會變成一個前面是$0$,後面是$1$的序列,因為$1$的位置已經確定了,所以剩下的$0$的位置就是$2$了;以此類推。

考慮用線段樹維護這個$01$區間中$0$和$1$的數量,依次為$1,1、2,1、2、3,1、2、3、4……$做線段樹,然後如上記錄答案。

程式碼

 1 #include <bits/stdc++.h>
 2 #define INF 0x3f3f3f3f
 3 using namespace std;
 4 int n, m, q[100010], sum[100010], l[100010], r[100010], x[100010], ans[100010];
 5 char s[100010];
 6 struct Tree {int l, r, val, lazy;}tree[800010];
 7 void build(int u, int l, int r)
 8 {
 9     int mid = (l + r) >> 1;
10     tree[u].l = l, tree[u].r = r, tree[u].lazy = -1
; 11 if(l == r) {tree[u].val = sum[l]; return;} 12 build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); 13 tree[u].val = tree[u << 1].val + tree[u << 1 | 1].val; 14 return; 15 } 16 void pushdown(int u) 17 { 18 if(!(~tree[u].lazy)) return; 19 int mid = (tree[u].l + tree[u].r) >> 1; 20 tree[u << 1].lazy = tree[u << 1 | 1].lazy = tree[u].lazy; 21 tree[u << 1].val = tree[u << 1].lazy * (mid - tree[u].l + 1), tree[u << 1 | 1].val = tree[u << 1 | 1].lazy * (tree[u].r - mid); 22 tree[u].lazy = -1; 23 return; 24 } 25 void change(int u, int l, int r, int x) 26 { 27 if(tree[u].l > r || tree[u].r < l) return; 28 if(tree[u].l >= l && tree[u].r <= r) {tree[u].lazy = x, tree[u].val = x * (tree[u].r - tree[u].l + 1); return;} 29 pushdown(u); 30 change(u << 1, l, r, x), change(u << 1 | 1, l, r, x); 31 tree[u].val = tree[u << 1].val + tree[u << 1 | 1].val; 32 return; 33 } 34 int query(int u, int l, int r) 35 { 36 if(tree[u].l > r || tree[u].r < l) return 0; 37 if(tree[u].l >= l && tree[u].r <= r) return tree[u].val; 38 pushdown(u); 39 return query(u << 1, l, r) + query(u << 1 | 1, l, r); 40 } 41 void solve(int num) 42 { 43 for(int i = 1; i <= n; ++i) sum[i] = (q[i] > num ? 1 : 0); 44 build(1, 1, n); 45 for(int i = 1; i <= m; ++i) 46 { 47 int cnt = query(1, l[i], r[i]); 48 if(x[i] == 1) change(1, l[i], r[i] - cnt, 0), change(1, r[i] - cnt + 1, r[i], 1); 49 else change(1, l[i], l[i] + cnt - 1, 1), change(1, l[i] + cnt, r[i], 0); 50 } 51 for(int i = 1; i <= n; ++i) 52 { 53 int cnt = query(1, i, i); 54 if(ans[i]) continue; 55 if(!cnt) ans[i] = num; 56 } 57 return; 58 } 59 int main() 60 { 61 scanf("%d %d %s", &n, &m, s + 1); 62 for(int i = 1; i <= n; ++i) q[i] = (int)s[i] - 'a' + 1; 63 for(int i = 1; i <= m; ++i) scanf("%d %d %d", &l[i], &r[i], &x[i]); 64 for(int i = 1; i <= 26; ++i) solve(i); 65 for(int i = 1; i <= n; ++i) printf("%c", (char)(ans[i] + 'a' - 1)); 66 return 0; 67 }