题目描述
思路
先忽视区间查询,比较好想到的是动态规划,设 分别表示 分别满足含有 子序列且没有 的方案数。
接下来大力转移:
- 的转移
需要删的时候,就是出现了 ,那么很容易得到转移为:
- 的转移
需要删的时候,就是前面出现了 ,并且这里出现了 ,或者这里第一次出现 ,那么很容易得到转移为:
- 的转移
需要删的时候,就是前面出现了 ,并且这里出现了 ,或者前面出现了 ,并且这里出现了 ,那么很容易得到转移为:
- 的转移
需要删的时候,就是前面出现了 ,并且这里出现了 或者 ,或者前面出现了 ,并且这里出现了 ,那么很容易得到转移为:
- 的转移
需要删的时候,就是前面出现了 ,并且这里出现了 ,或者前面出现了 ,并且这里出现了 ,那么很容易得到转移为:
然后转移就做完了。
观察到数据范围很大,考虑矩阵快速幂优化,很容易想到首先要新定义矩阵乘法:
然后,我们可以把动态规划写成矩阵:
定义运算 :
然后,我们由于要查询区间的答案,那么用 SMT 维护区间矩阵乘法,然后设区间 转移矩阵的积为 ,那么答案为:
然后就做完了,时间复杂度 ,常数极大。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN_Matrix = 5, MAXN = 2e5 + 10;
struct Matrix {
int M[MAXN_Matrix][MAXN_Matrix];
Matrix() {
memset(M, 0x3f, sizeof M);
}
Matrix friend operator*(Matrix x, Matrix y) {
Matrix res;
for (int i = 0; i < MAXN_Matrix; i++) {
for (int j = 0; j < MAXN_Matrix; j++) {
for (int k = 0; k < MAXN_Matrix; k++) {
res.M[i][j] = min(res.M[i][j], x.M[i][k] + y.M[k][j]);
}
}
}
return res;
}
Matrix friend operator^(Matrix x, Matrix y) {
Matrix res;
for (int i = 0; i < MAXN_Matrix; i++) {
for (int j = 0; j < MAXN_Matrix; j++) {
res.M[0][i] = min(res.M[0][i], x.M[0][j] + y.M[j][i]);
}
}
return res;
}
} SMT[MAXN << 2], Ans, Prime, G[MAXN];
void Build(int p, int l, int r) {
if (l == r) {
SMT[p] = G[l];
return;
}
int mid = l + r >> 1;
Build(p << 1, l, mid);
Build(p << 1 | 1, mid + 1, r);
SMT[p] = SMT[p << 1] * SMT[p << 1 | 1];
}
void Query(int p, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
Ans = Ans ^ SMT[p];
return;
}
int mid = l + r >> 1;
if (ql <= mid) Query(p << 1, l, mid, ql, qr);
if (mid < qr) Query(p << 1 | 1, mid + 1, r, ql, qr);
}
int N, P;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> N >> P;
Prime.M[0][0] = 0;
for (int i = 1; i <= N; i++) {
static char S;
cin >> S;
for (int j = 0; j < MAXN_Matrix; j++) G[i].M[j][j] = 0;
if (S == '2') {
G[i].M[0][0] = 1;
G[i].M[0][1] = 0;
}
if (S == '0') {
G[i].M[1][1] = 1;
G[i].M[1][2] = 0;
}
if (S == '1') {
G[i].M[2][2] = 1;
G[i].M[2][3] = 0;
}
if (S == '7') {
G[i].M[3][3] = 1;
G[i].M[3][4] = 0;
}
if (S == '6') {
G[i].M[3][3] = 1;
G[i].M[4][4] = 1;
}
}
Build(1, 1, N);
for (int i = 1; i <= P; i++) {
static int l, r;
cin >> l >> r;
Ans = Prime;
Query(1, 1, N, l, r);
cout << (Ans.M[0][4] <= N ? Ans.M[0][4] : -1) << endl;
}
return 0;
}