2019南昌网络赛 Hello 2019 (线段树优化dp)

22 篇文章 0 订阅

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

题目大意:给出一个字符串,每次查询区间只包含9102而不包含8102至少要删去多少字符(这里的包含是指子序列包含)。

解题思路:如果没有对区间查询的话,应该是个dp,由于是对区间的查询,并且可以分治,所以就考虑线段树转移dp了。

详情:TP

#include<bits/stdc++.h>
using namespace std;
#define LL long long

const int N = 2e5+5;
const int inf = 0x3f3f3f3f;

struct node
{
    int a[5][5];
    void init(){memset(a,inf,sizeof(a));}

}t[N*4];

node ans;

node Merge(node le,node ri)
{
    node tmp;
    tmp.init();
    for(int i=0;i<5;i++)
    {
        for(int j=i;j<5;j++)
        {
            for(int k=i;k<=j;k++)
            {
                tmp.a[i][j]=min(tmp.a[i][j],le.a[i][k]+ri.a[k][j]);
            }
        }
    }
    return tmp;
}
char s[N];
int num[N];

void build(int rt,int l,int r)
{
    if(l==r)
    {
        t[rt].init();
        for(int i=0;i<5;i++)t[rt].a[i][i]=0;
        if(num[l]==2){t[rt].a[0][0]=1;t[rt].a[0][1]=0;}
        if(num[l]==0){t[rt].a[1][2]=0;t[rt].a[1][1]=1;}
        if(num[l]==1){t[rt].a[2][2]=1;t[rt].a[2][3]=0;}
        if(num[l]==9){t[rt].a[3][3]=1;t[rt].a[3][4]=0;}
        if(num[l]==8){t[rt].a[3][3]=1;t[rt].a[4][4]=1;}
        return ;
    }
    int m=(l+r)>>1;
    build(rt<<1,l,m);
    build(rt<<1|1,m+1,r);
    t[rt]=Merge(t[rt<<1],t[rt<<1|1]);
}

void  query(int rt,int l,int r,int ql,int qr)
{
    if(l>=ql && r<=qr)
    {
        if(l==ql) ans=t[rt];
        else ans=Merge(ans,t[rt]);
        return ;
    }
    int m=(l+r)>>1;
    if(ql<=m) query(rt<<1,l,m,ql,qr);
    if(qr>m) query(rt<<1|1,m+1,r,ql,qr);
}

int pos[N];
int main()
{
    int n,m;
    cin>>n>>m;
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)num[i]=s[n-i+1]-'0';
    build(1,1,n);
    int now=n;
    for(int i=1;i<=(n+(n%2))/2;i++)
    {
        pos[i]=now;
        pos[now]=i;
        now--;
    }
    while(m--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        ans.init();
        query(1,1,n,pos[r],pos[l]);
        printf("%d\n",ans.a[0][4]==inf?-1:ans.a[0][4]);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值