传送门:C.Hello 2019
A digital string is “good”: when it contains a subsequence 9102 and does not contain a subsequence 8102.
The bad value of a string is defined as how many characters are to remove at least, so that the string satisfies the “good” property.
Output -1 if the string cannot satisfy the “good” property by removing some characters (0 or maybe more).
Input
The first line contains two integers n,Q(1≤n,Q≤2∗105). Where n is the length of the string and Q is the number of queries.
The second line contains a string s that consists entirely of decimal numbers.
The next Q line, each line contains two integers l,r(1≤l≤r≤n), denoting a query.
Output
For each query, output an answer which is the bad value of the substring slsl+1⋯sr from ss.
样例输入
8 3
88988102
1 8
2 8
1 7
样例输出
4
3
-1
这个题目和 Codeforces750E 这题一样,只是换了个数字 = - =
解题思路:
f[i][j]:表示状态 i -> j 的最小花费
0:什么都没有出现
1:出现2
2:出现20
3:出现201
4:出现2019
出现了2,20的时候,出现8不必管他
但是如果出现了201,2019后,在出现8我们就要把他删除
if(x==2)//出现2
{
f[0][0]=1;//从什么都没->什么都没,就要把出现的2删去
f[0][1]=0;//从什么都没->出现2,不需要操作
//f[1][1]=0;已经出现2或,再出现不需要操作,下同
}
else if(x==0)//出现0
{
f[1][1]=1;//从出现2->出现2,要把现在的0删去
f[1][2]=0;//从 2 -> 20,不需与操作,加入即可
}
else if(x==1)//出现1
{
f[2][2]=1;
f[2][3]=0;
}
else if(x==9)//出现9
{
f[3][3]=1;
f[3][4]=0;
}
else if(x==8)//出现8
{
f[3][3]=1;//出现201->出现8,删8
f[4][4]=1;//出现2019->出现8,删8
}
线段树加一个合并操作;
那个合并的操作类似于求最短路;
最后输出f[0][4];表示从什么都没有然后出现"2019"的最小操作数
AC代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<string.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=1100000;
int n,q,l,r;
int a[N];
char s[N];
/*
0:什么都没有出现
1:出现2
2:出现20
3:出现201
4:出现2019
*/
struct Node
{
int f[5][5];
int l,r;
void init(int x)
{
memset(f,inf,sizeof(f));
for(int i=0;i<=4;i++)
f[i][i]=0;
if(x==-1)//初始化
{
for(int i=0;i<=4;i++)
f[i][i]=inf;
}
if(x==2)
{
f[0][0]=1;//从什么都没->什么都没,就要把出现的2删去
f[0][1]=0;//从什么都没->出现2,不需要操作
//f[1][1]=0;
}
else if(x==0)
{
f[1][1]=1;
f[1][2]=0;
}
else if(x==1)
{
f[2][2]=1;
f[2][3]=0;
}
else if(x==9)
{
f[3][3]=1;
f[3][4]=0;
}
else if(x==8)
{
f[3][3]=1;//出现201->出现8,删8
f[4][4]=1;//出现2019->出现8,删8
}
}
}tree[4*N];
Node ans;
Node multi(Node a,Node b)
{
Node temp;
temp.init(-1);
//temp.l=a.l;temp.r=b.r;
for(int i=0;i<=4;i++)
{
for(int j=0;j<=4;j++)
{
for(int k=0;k<=4;k++)
{
temp.f[i][j]=min(temp.f[i][j],a.f[i][k]+b.f[k][j]);
}
}
}
return temp;
}
void build(int k,int l,int r)
{
tree[k].l=l;tree[k].r=r;
if(tree[k].l==tree[k].r)
{
tree[k].init(a[l]);
return;
}
int mid=(l+r)/2;
build(2*k,l,mid);
build(2*k+1,mid+1,r);
tree[k]=multi(tree[2*k],tree[2*k+1]);
tree[k].l=l;tree[k].r=r;
}
Node quary(int k,int l,int r)
{
if(tree[k].l>=l&&tree[k].r<=r)
{
return tree[k];
}
int mid=(tree[k].l+tree[k].r)/2;
if(r<=mid)//目标区间在当前区间的右边
return quary(2*k,l,r);
if(l>mid)//目标区间在当前区间的左边
return quary(2*k+1,l,r);
return multi(quary(2*k,l,mid),quary(2*k+1,mid+1,r));//目标区间在当前区间的中间
}
int main()
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(int i=1;i<=n;i++)//反转字符
a[i]=s[n-i+1]-'0';
build(1,1,n);
while(q--)
{
scanf("%d%d",&l,&r);
int tmp=l;
l=n-r+1;//反转后,区间端点也要变
r=n-tmp+1;
//printf("%d %d\n",l,r);
ans=quary(1,l,r);
if(ans.f[0][4]==inf) printf("-1\n");
else printf("%d\n",ans.f[0][4]);
}
return 0;
}