Good Bye 2016 E. New Year and Old Subsequence//线段树+区间dp

29 篇文章 0 订阅

题意:给字符串,q个询问[l,r],每次询问至少删除多少个字符使得[l,r]只含子串“2017”不含“2016” ,不要求连续。

思路:

每一个线段树节点维护dp[i][j],表示经过线段前已经有"2017"子串的i位,经过线段后有“2017”的j位,要删除的最少字符个数。

然后区间dp转移。注意细节。

/*   Author : Rshs
 *   Data : 2019-09-10-13.12
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const LL mod = 1e9+7;
const int MX = 2e5+5;
const int sz = 5;

int n,q;string s;
int ma[300];
struct no{int m[5][5];};
no T[MX<<2];
no ff(no x,no y){
    no re;
    for(int j=0;j<sz;j++) for(int k=0;k<sz;k++) re.m[j][k]=INT_MAX/10; //初始化为-1
    for(int i=0;i<sz;i++){
        for(int j=0;j<sz;j++){
            for(int k=0;k<=j;k++){
                re.m[i][j]=min(re.m[i][j],x.m[i][k]+y.m[k][j]); //左右转移
            }
        }
    }
    return re;
}
void pushUp(int rt){
    T[rt]=ff(T[rt<<1],T[rt<<1|1]);
}

void build(int l,int r,int rt){
    if(l==r){
        for(int j=0;j<sz;j++) for(int k=0;k<sz;k++) T[rt].m[j][k]=(k==j)?0:INT_MAX/10;//更新线段长度为1
        char now=s[l-1];
        if(now=='2') T[rt].m[0][0]=1,T[rt].m[0][1]=0;
        if(now=='0') T[rt].m[1][1]=1,T[rt].m[1][2]=0;
        if(now=='1') T[rt].m[2][2]=1,T[rt].m[2][3]=0;
        if(now=='7') T[rt].m[3][3]=1,T[rt].m[3][4]=0;
        if(now=='6') T[rt].m[4][4]=1,T[rt].m[3][3]=1;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushUp(rt);
}
no query(int L,int R,int l,int r,int rt){
    if(l>=L&&r<=R)
        return T[rt];
    int m=(l+r)>>1;
    if(m<L)
        return query(L,R,m+1,r,rt<<1|1);
    if(m>=R)
        return query(L,R,l,m,rt<<1);
    return ff(query(L,R,l,m,rt<<1),query(L,R,m+1,r,rt<<1|1));
}
int main(){
    cin>>n>>q;cin>>s;
    build(1,n,1);
    while(q--){
        int l,r;scanf("%d%d",&l,&r);
        int now=query(l,r,1,n,1).m[0][4];
        if(now==INT_MAX/10)puts("-1");
        else cout<<now<<'\n';
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值