题目
CF750E New Year and Old Subsequence
分析
设
dp[i][0/1/2/3/4] 表示前
i 个数字只能依次匹配到
ϕ/{2}/{2,0}/{2,0,1}/{2,0,1,7} 的最小删除代价,则
dp[i][0]dp[i][1]dp[i][2]dp[i][3]dp[i][4]={dp[i−1][0]dp[i−1][0]+1si=2si=2=⎩⎪⎨⎪⎧dp[i−1][1]dp[i−1][1]+1min{dp[i−1][0],dp[i−1][1]}si∈/{2,0}si=0si=2=⎩⎪⎨⎪⎧dp[i−1][2]dp[i−1][2]+1min{dp[i−1][1],dp[i−1][2]}si∈/{0,1}si=1si=0=⎩⎪⎨⎪⎧dp[i−1][3]dp[i−1][3]+1min{dp[i−1][2],dp[i−1][3]}si∈/{1,7,6}si∈{7,6}si=1=⎩⎪⎨⎪⎧dp[i−1][4]dp[i−1][4]+1min{dp[i−1][3],dp[i−1][4]}si∈/{7,6}si=6si=7 构造转移矩阵
Mi 使得
Mi×⎝⎜⎜⎜⎜⎛dp[i−1][0]dp[i−1][1]dp[i−1][2]dp[i−1][3]dp[i−1][4]⎠⎟⎟⎟⎟⎞=⎝⎜⎜⎜⎜⎛dp[i][0]dp[i][1]dp[i][2]dp[i][3]dp[i][4]⎠⎟⎟⎟⎟⎞ 其中矩阵乘法定义改变
+→min,×→+。
不难发现
Mi=⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧⎝⎜⎜⎜⎜⎛10+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0⎠⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎛0+∞+∞+∞+∞+∞10+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0⎠⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎛0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞10+∞+∞+∞+∞0+∞+∞+∞+∞+∞0⎠⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎛0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞10+∞+∞+∞+∞0⎠⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎛0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞1+∞+∞+∞+∞+∞1⎠⎟⎟⎟⎟⎞⎝⎜⎜⎜⎜⎛0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0+∞+∞+∞+∞+∞0⎠⎟⎟⎟⎟⎞si=2si=0si=1si=7si=6si∈/{2,0,1,7,6}
线段树上维护矩阵乘法即可。于是这道题可以单点修改
错因
- 矩阵乘法没有交换律,所以根据我们矩阵乘法的规则,又
Mi 在左边,所以从右往左乘才是正常的 DP 过程。因此
PushUp
和 Query
中都是 rch
乘 lch
而不是 lch
乘 rch
。
代码
#include <algorithm>
#include <cstdio>
#include <cstring>
const int INF = 0x3f3f3f3f;
const int MAXN = 200000;
const int MAXP = 5;
int N, Q;
char S[MAXN + 5];
struct Matrix {
int n, m;
int Mat[MAXP + 1][MAXP + 1];
Matrix(int _n = 0, int _m = 0) {
n = _n, m = _m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
Mat[i][j] = INF;
}
int* operator [] (const int &i) {
return Mat[i];
}
Matrix operator * (Matrix other) {
Matrix ret(n, other.m);
for (int i = 1; i <= ret.n; i++)
for (int j = 1; j <= ret.m; j++)
for (int k = 1; k <= m; k++)
ret[i][j] = std::min(ret[i][j], Mat[i][k] + other[k][j]);
return ret;
}
void Debug() {
for (int i = 1; i <= n; i++, puts(""))
for (int j = 1; j <= m; j++) {
if (Mat[i][j] == INF)
printf("INF\t");
else
printf("%d\t", Mat[i][j]);
}
}
};
struct SegmentTree {
#define lch (u << 1)
#define rch (u << 1 | 1)
Matrix Dp[(MAXN << 2) + 5];
void PushUp(int u) {
Dp[u] = Dp[rch] * Dp[lch];
}
void Build(int u, int lft, int rgt) {
if (lft == rgt) {
Dp[u] = Matrix(5, 5);
for (int i = 1; i <= 5; i++)
Dp[u][i][i] = 0;
switch (S[lft]) {
case '2': Dp[u][1][1] = 1, Dp[u][2][1] = 0; break;
case '0': Dp[u][2][2] = 1, Dp[u][3][2] = 0; break;
case '1': Dp[u][3][3] = 1, Dp[u][4][3] = 0; break;
case '7': Dp[u][4][4] = 1, Dp[u][5][4] = 0; break;
case '6': Dp[u][4][4] = 1, Dp[u][5][5] = 1; break;
}
return;
}
int mid = (lft + rgt) >> 1;
Build(lch, lft, mid);
Build(rch, mid + 1, rgt);
PushUp(u);
}
Matrix Query(int u, int lft, int rgt, int l, int r) {
if (l <= lft && rgt <= r)
return Dp[u];
int mid = (lft + rgt) >> 1;
if (mid < l)
return Query(rch, mid + 1, rgt, l, r);
if (r <= mid)
return Query(lch, lft, mid, l, r);
return Query(rch, mid + 1, rgt, l, r) * Query(lch, lft, mid, l, r);
}
#undef lch
#undef rch
}T;
int main() {
scanf("%d%d%s", &N, &Q, S + 1);
T.Build(1, 1, N);
while (Q--) {
int l, r;
scanf("%d%d", &l, &r);
Matrix Ans = T.Query(1, 1, N, l, r);
if (Ans[5][1] >= INF)
puts("-1");
else
printf("%d\n", Ans[5][1]);
}
return 0;
}