题目链接:https://www.jisuanke.com/contest/3870?view=challenges
解题思路:
也就是CF这道题反着过来,这一题变成和右结合。
道理还是五个状态,反过来看:
1、2都没有
2、2后面没有接0
3、20后面没有接1
4、201后面没接9
5、有2019
然后用dp[i][j]表示从i转态转移到j转态的最少花费,我们一开始当然是0转态了,可以看做一个空区间合并查询区间。
然后注意8的花费,然后就是线段树合并了。
#include <bits/stdc++.h>
#define mid (l+r>>1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
int n,m;
char str[mx];
struct matrix {
int a[5][5];
matrix () { memset(a,inf,sizeof(a)); }
matrix operator * (matrix A) {
matrix ret;
for (int k=0;k<5;k++){
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
ret.a[i][j] = min(ret.a[i][j],a[i][k]+A.a[k][j]);
}
}
}
return ret;
}
void init(int x){
for (int i=0;i<5;i++) a[i][i] = 0;
if (x==2){
a[0][0] = 1;
a[0][1] = 0;
}else if(x==0){
a[1][1] = 1;
a[1][2] = 0;
}else if(x==1){
a[2][2] = 1;
a[2][3] = 0;
}else if(x==9){
a[3][3] = 1;
a[3][4] = 0;
}else if(x==8){
a[3][3] = 1;
a[4][4] = 1;
}
}
}s[mx<<2];
void build(int l,int r,int rt)
{
if (l==r){
s[rt].init(str[l]-'0');
return ;
}
build(lson);build(rson);
s[rt] = s[rt<<1|1] * s[rt<<1];
}
matrix query(int l,int r,int rt,int L,int R)
{
if (L<=l&&r<=R) return s[rt];
if (L<=mid&&R>mid) return query(rson,L,R)*query(lson,L,R);
if (R>mid) return query(rson,L,R);
return query(lson,L,R);
}
int main() {
scanf("%d%d",&n,&m);
scanf("%s",str+1);
build(1,n,1);
int l,r;
while(m--){
scanf("%d%d",&l,&r);
matrix ans = query(1,n,1,l,r);
printf("%d\n",ans.a[0][4]==inf?-1:ans.a[0][4]);
}
return 0;
}