我们考虑5种状态的矩阵:
0–什么都没出现
1–出现了2
2–出现了20
3–出现了201
4–出现了2017
重定义 加法为取min,乘法为 加,每个点根据字符可以得到一个初始矩阵
那么答案就是l~r的矩阵的乘积。因为矩阵乘法满足结合律,因此我们可以用线段树分治来快速求区间矩阵乘积。
复杂度
O((n+m)logn52)
O
(
(
n
+
m
)
l
o
g
n
5
2
)
另外这样可以加强到带单点修改,还可以不只有删除操作,还可以有花给定代价修改一个字符,求最小代价。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 200010
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
char s[N];
inline void get_S(){
char ch=gc();int tot=0;
while(ch<'0'||ch>'9') ch=gc();
while(ch>='0'&&ch<='9') s[++tot]=ch,ch=gc();
}
int n,m;
struct node{
int a[5][5];//0-- null 1-- 2 2-- 20 3-- 201 4--2017
int* operator[](int x){return a[x];}
inline void init(){memset(a,inf,sizeof(a));}
friend node operator+(node a,node b){
node res;res.init();
for(int i=0;i<5;++i)
for(int j=0;j<5;++j)
for(int k=0;k<5;++k)
res[i][j]=min(res[i][j],a[i][k]+b[k][j]);
return res;
}
}tr[N<<2];
inline void build(int p,int l,int r){
if(l==r){
tr[p].init();
for(int i=0;i<5;++i) tr[p][i][i]=0;
if(s[l]=='2'){
tr[p][0][1]=0;
tr[p][0][0]=1;return;
}if(s[l]=='0'){
tr[p][1][2]=0;
tr[p][1][1]=1;return;
}if(s[l]=='1'){
tr[p][2][3]=0;
tr[p][2][2]=1;return;
}if(s[l]=='6'){
tr[p][3][3]=1;
tr[p][4][4]=1;return;
}if(s[l]=='7'){
tr[p][3][4]=0;
tr[p][3][3]=0;return;
}return;
}int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
inline node ask(int p,int l,int r,int x,int y){
if(x==l&&r==y) return tr[p];int mid=l+r>>1;
if(y<=mid) return ask(p<<1,l,mid,x,y);
if(x>mid) return ask(p<<1|1,mid+1,r,x,y);
return ask(p<<1,l,mid,x,mid)+ask(p<<1|1,mid+1,r,mid+1,y);
}
int main(){
// freopen("a.in","r",stdin);
n=read();m=read();
get_S();build(1,1,n);
while(m--){
int x=read(),y=read();
int res=ask(1,1,n,x,y).a[0][4];
if(res==inf) puts("-1");
else printf("%d\n",res);
}return 0;
}