Hello 2019 (线段树维护矩阵实现任意区间的dp)

A digital string is "good": when it contains a subsequence 91029102 and does not contain a subsequence 81028102.

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, Qn,Q(1\leq n,Q\leq2*10^5)(1≤n,Q≤2∗105). Where nn is the length of the string and QQ is the number of queries.

The second line contains a string ss that consists entirely of decimal numbers.

The next QQ line, each line contains two integers l, rl,r(1\leq l\leq r\leq n)(1≤l≤r≤n), denoting a query.

Output

For each query, output an answer which is the bad value of the substring s_ls_{l+1} \cdots s_rsl​sl+1​⋯sr​ from ss.

样例

8 3
88988102
1 8
2 8
1 7

样例

4
3
-1

2019年icpc南昌网络赛C题

题意:给了一个长度为n只包含数字的字符串,m个区间询问,问对于每一次区间询问 [ l , r ]  需要去掉多少个字符才能使区间有9102没有8102.  反过来看的话就是只有 2019 没有 2018.  如果不能达成,则输出 -1.

思路:对于总区间来说,求最少的去掉个数就是一个dp。dp[ i ][ j ] 表示从 i 状态到达 j 状态的最小花费。由于只有5个字符对结果有影响,因此,将2重定义为0、0重定义为1、1重定义为2、9重定义为3、8重定义为4;又因为任意的dp都可以转化成矩阵来维护,而我们要查询的是区间的dp值,所以我们使用线段树来维护矩阵。

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define N 200010
using namespace std;
char s[N];
struct node{
    int a[5][5];
    node(){memset(a,inf,sizeof(a));}
    node operator * (node b){
        node ans;
        for(int k=0;k<5;k++){
            for(int i=0;i<5;i++){
                for(int j=0;j<5;j++){
                    ans.a[i][j]=min(ans.a[i][j],a[i][k]+b.a[k][j]);
                }
            }
        }
        return ans;
    }
    void push(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;
    }
}Sum[N<<2];

void build(int l,int r,int o){
    if(l==r){
        Sum[o].push(s[l]-'0');
        return;
    }
    int mid = (l + r)>>1;
    build(l,mid,o<<1);
    build(mid+1,r,o<<1|1);
    Sum[o] = Sum[o<<1|1] * Sum[o<<1];
}

node query(int x,int y,int l,int r,int o){
    if(l>=x&&r<=y)return Sum[o];
    int mid=(l+r)>>1;
    if(x<=mid&&y>mid) return query(x,y,mid+1,r,o<<1|1)*query(x,y,l,mid,o<<1);
    if(y>mid)return query(x,y,mid+1,r,o<<1|1);
    return query(x,y,l,mid,o<<1);
}


inline int read() {
    int x = 0 , f = 1 ; char c = getchar() ;
    while( c < '0' || c > '9' ) { if( c == '-' ) f = -1 ; c = getchar() ; }
    while( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar() ; }
    return x * f ;
}

int main(){
    int n,q;
    n=read();
    q=read();
    scanf("%s",s+1);
    build(1,n,1);
    for(int i=1;i<=q;i++){
        int l,r;
        l=read();
        r=read();
        int ans=query(l,r,1,n,1).a[0][4];
        printf("%d\n",ans==inf?-1:ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值