2019 Asia Nanchang First Round Online C. Hello 2019

题目链接: https://nanti.jisuanke.com/t/41350

题意:

给你一个长为 2 e 6 2e6 2e6 的字符串,给你 2 e 6 2e6 2e6 个询问,每次询问一个区间 [ l , r ] [l,r] [l,r] ,问你在这个区间中,最少要删掉多少个字符才能使得这个区间中存在子序列 ′ 910 2 ′ '9102' 9102 而不存在子序列 ‘ 8102 ’ ‘8102’ 8102

做法:

这里先贴一道cf上一个很相似的原题:https://codeforc.es/contest/750/problem/E。

这道题只要把条件转换一下,字符串前后倒置就是一样的做法了。

这里建立了一个长为 n n n 的线段树,每棵线段树维护矩阵乘法,复杂度 25 n l o g n 25nlogn 25nlogn

什么叫维护矩阵乘法呢,其实应该更像加法的样子。如果我们没有这个所谓的区间查询,只是问你整个序列的话,其实就是一个 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示字符在匹配从 i i i 这个位置到 j j j 这个位置的所删最少字符个数。

什么叫位置,我们将数字 01234 01234 01234 表示位置。

| 2 | 0 | 1 | 7 |
0–1-- 2 --3–4 (横线只是为了对齐)

线段树的每个节点存一个矩阵 A A A a i , j a_{i,j} ai,j表示使原串的子序列包含 ′ 201 9 ′ '2019' 2019 中第 i i i 个间隔到第 j j j 个间隔组成的子串,但不包含严格包含它的子序列最少需要删除的数字.

如果这个位置上的字符是 ′ 1 ′ '1' 1,那么为了保证在出现位置 2 2 2 开始之后结尾还是在 2 2 2 ,就要删掉这个字符,即 a [ 2 ] [ 2 ] = 1 a[2][2] =1 a[2][2]=1 ,并且 a [ 2 ] [ 3 ] = 0 a[2][3] =0 a[2][3]=0

然后就是加法进行转移了!因为我们是反了一下,所以查询的时候也转一下就好了。

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2

using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=200005;
const int N=5;

struct node{
    int a[N][N];
    node operator + (node x){
        node ret;
        rep(i,0,4){
            rep(j,0,4){
                ret.a[i][j]=maxn;
                rep(k,0,4){
                    ret.a[i][j]=min(ret.a[i][j],a[i][k]+x.a[k][j]);
                }
            }
        }
        return ret;
    }
}e[maxn<<2];
char s[maxn];
int n,q;
void build(int l,int r,int rt){
    if(l==r){
        rep(i,0,4){
            rep(j,0,4){
                e[rt].a[i][j]=(i==j?0:maxn);
            }
        }
        if(s[l]=='2') e[rt].a[0][1]=0,e[rt].a[0][0]=1;
        if(s[l]=='0') e[rt].a[1][2]=0,e[rt].a[1][1]=1;
        if(s[l]=='1') e[rt].a[2][3]=0,e[rt].a[2][2]=1;
        if(s[l]=='9') e[rt].a[3][4]=0,e[rt].a[3][3]=1;
        if(s[l]=='8') e[rt].a[3][3]=1,e[rt].a[4][4]=1;
        return ;
    }
    build(l,mid,lson);
    build(mid+1,r,rson);
    e[rt]=e[lson]+e[rson];
}
node query(int l,int r,int rt,int ql,int qr){
    if(ql<=l&&r<=qr){
        return e[rt];
    }

    if(ql>mid) return query(mid+1,r,rson,ql,qr);
    else if(qr<=mid) return query(l,mid,lson,ql,qr);
    else return query(l,mid,lson,ql,qr)+query(mid+1,r,rson,ql,qr);
}
int main(){
    scanf("%d%d%s",&n,&q,s+1);
    for(int l=1,r=n;l<=r;l++,r--) swap(s[l],s[r]);
    build(1,n,1);
    while(q--){
        int l,r; scanf("%d%d",&l,&r);
        int ll=n-r+1,rr=n-l+1;
        node x=query(1,n,1,ll,rr);
        int ans=x.a[0][4];
        if(ans==maxn) printf("-1\n");
        else printf("%d\n",ans);

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值