CF750E New Year and Old Subsequence
只需考虑 $2017$ 的状态.
令 $0$ 表示没开始,$1$ 表示有 $2$, $2$ 表示有 $20$.....
令 $\mathrm{f[i][sta]}$ 表示 $\mathrm{DP}$ 到 $\mathrm{i}$, 状态为 $\mathrm{sta}$ 的最小删除步数.
然后在进行状态转移的时候发现每次只和 $\mathrm{sta}$ 有关,而这个 $\mathrm{sta}$ 种类却很少.
转移是固定的,而起点也是容易得知的,故可以利用线段树维护矩阵乘法来做.
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define N 200009
#define ll long long
#define pb push_back
#define ls now << 1
#define rs now << 1 | 1
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
const int inf = 100000000;
char str[N];
int n,Q,A[N];
struct M {
int c[6][6];
M() { memset(c,0x3f,sizeof(c));}
M operator*(const M b) const {
M fi;
for(int i=0;i<5;++i) {
for(int j=0;j<5;++j) {
for(int k=0;k<5;++k)
fi.c[i][j]=min(fi.c[i][j], c[i][k]+b.c[k][j]);
}
}
return fi;
}
}s[N<<2],G[N];
void build(int l,int r,int now) {
if(l == r) {
s[now] = G[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,ls);
build(mid+1,r,rs);
s[now]=s[ls]*s[rs];
}
M query(int l,int r,int now,int L,int R) {
if(l>=L&&r<=R) return s[now];
int mid=(l+r)>>1;
if(L<=mid&&R>mid) return query(l,mid,ls,L,R)*query(mid+1,r,rs,L,R);
else if(L<=mid) return query(l,mid,ls,L,R);
else return query(mid+1,r,rs,L,R);
}
int main() {
// setIO("input");
scanf("%d%d",&n,&Q);
scanf("%s",str+1);
for(int i=1;i<=n;++i) {
A[i]=str[i]-'0';
// 构建当前的矩阵.
memset(G[i].c, 0x3f, sizeof(G[i].c));
G[i].c[0][0]=(A[i]==2);
G[i].c[1][1]=(A[i]==0);
G[i].c[2][2]=(A[i]==1);
G[i].c[3][3]=(A[i]==6||A[i]==7);
G[i].c[4][4]=(A[i]==6);
if(A[i]==2) G[i].c[0][1]=0;
if(A[i]==0) G[i].c[1][2]=0;
if(A[i]==1) G[i].c[2][3]=0;
if(A[i]==7) G[i].c[3][4]=0;
}
build(1, n, 1);
for(int i=1;i<=Q;++i) {
int l,r;
scanf("%d%d",&l,&r);
M fin = query(1, n, 1, l, r);
int cur = fin.c[0][4];
if(cur <= n) {
printf("%d\n", cur);
}
else {
printf("-1\n");
}
}
return 0;
}

浙公网安备 33010602011771号