【題解】 CF750E New Year and Old Subsequence 動態dp
Legend
Link \(\textrm{to Codeforces}\).
給定長度為 \(n\ (4 \le n \le 200\ 000)\) 的數字串,\(q\ (1 \le q \le 200\ 000)\) 次詢問,每次詢問一個連續子串 \([l,r]\),詢問:
- 使得這個連續子串內不存在子序列 \(2016\) 並存在子序列 \(2017\) 至少要刪去多少個數位。
Editorial
考慮暴力怎麼做,有個很顯然的在 \(\textrm{trie}\) 上 \(\textrm{dp}\) 的做法(這個 \(\textrm{trie}\) 的大小隻有 \(5\)),即記錄走到某個節點的最少刪除次數。
那麼你只需要把這個 \(\textrm{dp}\) 用矩陣轉移優化一下就好了。
\[\begin{bmatrix} [s_i = 2] & \infty & \infty & \infty & \infty \\ [s_i \not= 2]\infty & [s_i = 0] & \infty & \infty & \infty \\ \infty & [s_i \not= 0]\infty & [s_i=1] &\infty & \infty \\ \infty & \infty & [s_i\not = 1]\infty & [s_i=6\or s_i=7] & \infty \\ \infty & \infty & \infty & [s_i \not= 7]\infty & [s_i=6] \\ \end{bmatrix} \times \begin{bmatrix} ST \\ a_2 \\ a_0 \\ a_1 \\ a_7 \\ \end{bmatrix} = \]
Code
注意矩陣的相乘順序,所以線段樹裡維護的因當是從區間右側乘到左側的矩陣。但查詢的時候依然是從左子樹到右子樹。
注意到右側乘的是一個向量,所以在查詢的時候可以省一些常數,由於我太懶,沒有去實現。
#include <bits/stdc++.h> using namespace std; const int INF = 1e9; const int MX = 2e5 + 233; int read(){ char k = getchar(); int x = 0; while(k < '0' || k > '9') k = getchar(); while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar(); return x; } struct Matrix{ int A[5][5]; Matrix(){memset(A ,0x3f ,sizeof A);} Matrix(int num){ memset(A ,0x3f ,sizeof A); A[0][0] = (num == 2); A[1][0] = (num != 2) * INF; A[1][1] = (num == 0); A[2][1] = (num != 0) * INF; A[2][2] = (num == 1); A[3][2] = (num != 1) * INF; A[3][3] = (num == 6 || num == 7); A[4][3] = (num != 7) * INF; A[4][4] = (num == 6); } Matrix operator *(const Matrix &B)const{ Matrix C; for(int i = 0 ; i < 5 ; ++i) for(int j = 0 ; j < 5 ; ++j) for(int k = 0 ; k < 5 ; ++k) C.A[i][j] = min(C.A[i][j] ,A[i][k] + B.A[k][j]); return C; } void output(){ for(int i = 0 ; i < 5 ; ++i) for(int j = 0 ; j < 5 ; ++j) printf("%d%c" ,A[i][j] ," \n"[j == 4]); puts("||||||||||||||||||||||||||||||||||||||||||"); } }; struct node{ int l ,r; Matrix s; node *lch ,*rch; node(int _l ,int _r ,int num ,node *L ,node *R){ s = Matrix(num); l = _l ,r = _r; lch = L ,rch = R; } void pushup(){s = rch->s * lch->s;} }*root; node *build(int l ,int r ,int *A){ node *x = nullptr; if(l == r) x = new node(l ,r ,A[l] ,nullptr ,nullptr); else{int mid = (l + r) >> 1; node *lch = build(l ,mid, A); node *rch = build(mid + 1 ,r ,A); x = new node(l ,r ,-1 ,lch ,rch); x->pushup(); }return x; } void query(node *x ,int l ,int r ,Matrix &Ans){ if(l <= x->l && x->r <= r) return Ans = x->s * Ans ,void(); if(l <= x->lch->r) query(x->lch ,l ,r ,Ans); if(r > x->lch->r) query(x->rch ,l ,r ,Ans); } char str[MX]; int A[MX]; int main(){ int n = read() ,q = read(); cin >> (str + 1); for(int i = 1 ; i <= n ; ++i){ A[i] = str[i] - '0'; // printf("%d\n" ,A[i]); } root = build(1 ,n ,A); while(q--){ int l = read() ,r = read(); Matrix Ans = Matrix(); Ans.A[0][4] = 0; query(root ,l ,r ,Ans); // Ans.output(); printf("%d\n" ,Ans.A[4][4] > n ? -1 : Ans.A[4][4]); } return 0; }