传送门
题意:在s[l~r]中减少最少的字符数,使得得到的字符串含有2017子序列,不含2016子序列。一共q次查询。
思路:设状态0表示空串,状态1表示走到了状态”2”,状态2表示都到了”20”,状态3表示走到”201”,状态4表示走到”2017”。
设f[i,j]表示在走到该区间之前走到了状态i,且在走完该区间后走到了状态j,最少需要删掉多少个字符。
如果知道左右区间的状态的话这两个矩阵显然是可以合并的。
那么只要用线段树来维护这个dp的转移即可。
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=200005;
char s[N];
struct node
{
int mat[5][5];
node operator + (node x)
{
node ans;
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
{
ans.mat[i][j]=N;
for(int k=0;k<5;k++)
{
ans.mat[i][j]=min(ans.mat[i][j],mat[i][k]+x.mat[k][j]);
}
}
}
return ans;
}
}a[N<<2];
void build(int l,int r,int rt)
{
if(l==r)
{
for(int i=0;i<5;i++)
{
for(int j=0;j<5;j++)
{
a[rt].mat[i][j]=(i==j)?0:N;
}
}
if(s[l]=='2')
a[rt].mat[0][1]=0,a[rt].mat[0][0]=1;
if(s[l]=='0')
a[rt].mat[1][2]=0,a[rt].mat[1][1]=1;
if(s[l]=='1')
a[rt].mat[2][3]=0,a[rt].mat[2][2]=1;
if(s[l]=='7')
a[rt].mat[3][4]=0,a[rt].mat[3][3]=1;
if(s[l]=='6')
a[rt].mat[3][3]=1,a[rt].mat[4][4]=1;
return;
}
int mid=(l+r)/2;
build(lson);
build(rson);
a[rt]=a[rt<<1]+a[rt<<1|1];
}
node query(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R)
return a[rt];
int m=(l+r)/2;
if(m<L)
return query(m+1,r,L,R,rt<<1|1);
if(m>=R)
return query(l,m,L,R,rt<<1);
return query(l,m,L,R,rt<<1)+query(m+1,r,L,R,rt<<1|1);
}
int main()
{
int n,q;
while(~scanf("%d%d%s",&n,&q,s+1))
{
build(1,n,1);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
int ans=query(1,n,l,r,1).mat[0][4];
if(ans==N)
ans=-1;
printf("%d\n",ans);
}
}
return 0;
}