题目链接:CF750E New Year and Old Subsequence
E. New Year and Old Subsequence
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
A string t is called nice if a string "2017" occurs in t as a subsequence but a string "2016" doesn't occur in t as a subsequence. For example, strings "203434107" and "9220617" are nice, while strings "20016", "1234" and "20167" aren't nice.
The ugliness of a string is the minimum possible number of characters to remove, in order to obtain a nice string. If it's impossible to make a string nice by removing characters, its ugliness is - 1.
Limak has a string s of length n, with characters indexed 1 through n. He asks you q queries. In the i-th query you should compute and print the ugliness of a substring (continuous subsequence) of s starting at the index _a__i_ and ending at the index _b__i_ (inclusive).
Input
The first line of the input contains two integers n and q (4 ≤ n ≤ 200 000, 1 ≤ q ≤ 200 000) — the length of the string s and the number of queries respectively.
The second line contains a string s of length n. Every character is one of digits '0'–'9'.
The i-th of next q lines contains two integers _a__i_ and _b__i_ (1 ≤ _a__i_ ≤ _b__i_ ≤ n), describing a substring in the i-th query.
Output
For each query print the ugliness of the given substring.
Examples
Input
Copy
8 3
20166766
1 8
1 7
2 8
Output
Copy
4
3
-1
Input
Copy
15 5
012016662091670
3 4
1 14
4 15
1 13
10 15
Output
Copy
-1
2
1
-1
-1
Input
Copy
4 2
1234
2 4
1 2
Output
Copy
-1
-1
Note
In the first sample:
- In the first query, ugliness("20166766") = 4 because all four sixes must be removed.
- In the second query, ugliness("2016676") = 3 because all three sixes must be removed.
- In the third query, ugliness("0166766") = - 1 because it's impossible to remove some digits to get a nice string.
In the second sample:
- In the second query, ugliness("01201666209167") = 2. It's optimal to remove the first digit '2' and the last digit '6', what gives a string "010166620917", which is nice.
- In the third query, ugliness("016662091670") = 1. It's optimal to remove the last digit '6', what gives a nice string "01666209170".
题意: 在区间\([l,r]\)中删去最少的字符数使得在这个区间内不含有子序列\("2016"\)且含有子序列\("2017"\),如果无法满足条件,输出\(-1\).
题解: 首先看到题目中要求区间的某个值,想到用某种数据结构来维护这个值.
然而这个最小值似乎是要用\(DP\)来求的?
那么这里就有点动态\(DP\)的意思了:我们将某个位置的状态加入矩阵中,再对这个序列开一棵线段树,线段树中的节点维护矩阵的状态.
我们将\("2017"\)拆成5份,分别是\(\empty,2,20,201,2017\),用\(0 \to 4\)表示这\(5\)个状态.
我们设转移矩阵\[D= \left[ \begin{matrix} a_{0,0} & ... & a_{0,4} \\ a_{i,j-1} & a_{i,j} & a_{i,j+1} \\ a_{4,0} & ... & a_{4,4} \end{matrix} \right] \tag{3} \]
其中\(a_{i,j}\)表示从\(i\)状态转移到\(j\)状态所需要的删去的字符数.
那么当我们枚举到一个位置的时候,假设这个位置是\(2\),那么如果不删去字符显然会形成状态\(1\),为了维持之前的状态\(0\),则需要删去这个字符,删去的字符数为1.那么对于某一位为\(2\),显然这位的转移矩阵为\[D= \left[ \begin{matrix} 1 & 0 & \inf & \inf & \inf \\ \inf & 0 & \inf & \inf & \inf \\ \inf & \inf & 0 & \inf & \inf \\ \inf & \inf & \inf & 0 & \inf \\ \inf & \inf & \inf & \inf & 0 \end{matrix} \right] \tag{3} \]
同理,对于该位为\(0,1,7\)都是一样的.
但是如果该位为\(6\)呢?显然我们是不能让串中出现\("2016"\)的,所以是一定要删除这个字符的,所以它的转移矩阵为\[D= \left[ \begin{matrix} 0 & \inf & \inf & \inf & \inf \\ \inf & 0 & \inf & \inf & \inf \\ \inf & \inf & 0 & \inf & \inf \\ \inf & \inf & \inf & 1 & \inf \\ \inf & \inf & \inf & \inf & 1 \end{matrix} \right] \tag{3} \]
也就是说,如果串中已经出现了\("201"\)或是\("2017"\),那么这个\(6\)就必须要删掉(或是其他位置删掉某个数字).
但是我们知道,矩阵乘法的运算法则是\(c_{i,j}=\sum_{k=1}^{n}a_{i,k}*b_{k,j}\),而我们这里是需要求一个最小值的,所以我们可以改一下矩阵的运算方法:\(c_{i,j}=max\{a_{i,k}+b_{k,j}\},k\in[1,n]\)
至于为什么这样是成立的,可以类比一下floyed求最短路更新的过程,因为这样的运算也是满足结合律的.
但是有点要注意,也就是矩阵乘法是不满足交换律的,也就是\(A*B\)不一定等于\(B*A\),而线段树的\(pushup\)一般是将左儿子的矩阵乘到右儿子的矩阵上,所以要注意一下哪个矩阵放在左边(虽然这题矩阵的构造方法是统一的,也就是说这题不用考虑这个问题)
看代码理解一下吧.
#include<bits/stdc++.h>
#define ll(x) (x << 1)
#define rr(x) (x << 1 | 1)
using namespace std;
const int N = 2e5+5;
const int inf = 0x3f3f3f3f;
int n, m;
char s[N];
struct Matrix{
int a[5][5];
Matrix(){ memset(a, 0x3f, sizeof(a)); }
Matrix operator * (Matrix x){
Matrix res;
for(int i = 0; i < 5; i++)
for(int j = 0; j < 5; j++)
for(int k = 0; k < 5; k++)
res.a[i][j] = min(res.a[i][j], a[i][k]+x.a[k][j]);
return res;
}
};
struct SegmentTree{
int l, r; Matrix M;
}t[N*4];
void up(int x){ t[x].M = t[ll(x)].M*t[rr(x)].M; }
void build(int x, int l, int r){
t[x].l = l, t[x].r = r; int mid = (l+r>>1);
if(l == r){
for(int i = 0; i < 5; i++) t[x].M.a[i][i] = 0;
if(s[l] == '2') t[x].M.a[0][0] = 1, t[x].M.a[0][1] = 0;
if(s[l] == '0') t[x].M.a[1][1] = 1, t[x].M.a[1][2] = 0;
if(s[l] == '1') t[x].M.a[2][2] = 1, t[x].M.a[2][3] = 0;
if(s[l] == '7') t[x].M.a[3][3] = 1, t[x].M.a[3][4] = 0;
if(s[l] == '6') t[x].M.a[3][3] = 1, t[x].M.a[4][4] = 1;
return;
}
build(ll(x), l, mid), build(rr(x), mid+1, r); up(x);
}
Matrix query(int x, int l, int r){
if(l <= t[x].l && t[x].r <= r) return t[x].M;
int mid = (t[x].l+t[x].r>>1);
if(r <= mid) return query(ll(x), l, r);
if(mid < l) return query(rr(x), l, r);
return query(ll(x), l, r)*query(rr(x), l, r);
}
int main(){
ios::sync_with_stdio(false);
int x, y; cin >> n >> m >> (s+1);
build(1, 1, n);
for(int i = 1; i <= m; i++){
cin >> x >> y;
Matrix ans = query(1, x, y);
cout << (ans.a[0][4] > n ? -1 : ans.a[0][4]) << endl;
}
return 0;
}