New Year and Old Subsequence【矩阵+线段树+DP】

题意:

给出一串数字序列,每次询问对于子串 ([l,r]) 至少删除多少个数字才能保证该子串无子序列 ('2016') 但有子序列 ('2017')
(4 ≤ n ≤ 200 000, 1 ≤ q ≤ 200 000)
题目链接:https://codeforces.com/contest/750/problem/E

分析:

分别用数字表示以下状态:
(0:emptyset)
(1:'2')
(2:'20')
(3:'201')
(4:'2017')
构造一个 (5 imes 5) 的转移矩阵,(mat[i][j]) 表示 (i) 状态是否可以转移到 (j) 状态,如果可以则保存最小花费。而且,二者之间可以通过一个中间状态 (k) 来转换。通过矩阵的加法来去最小。
对于转换矩阵先初始为:除正对角线上为 (0),其余都为 (inf)。因为初始的时候状态 (i) 只能转换到状态 (i) 不需要删字符,转换到其他状态都是不可达的。
遍历到第 (x) 个字符 (s[x]) 时,对 (s[x]) 的情况进行讨论:
s[x]=‘2’:字符 (2) 只会影响状态 (0)→状态 (1) 的过程,因此由状态 (0) ->状态 (1) 不需要删字符,即 (mat[0][1]=0),但由状态 (0)->状态 (0),已经被破坏,因此为了维持状态 (0)->状态
(0) ,需要将字符 (2) 删除的结果存放在转换矩阵中,即 (mat[0][0]=1);而剩下的状态都不能加上字符 (2) 变成其余状态,故都为 (inf)
s[x]=‘0’:字符 (0) 会影响状态 (1)->状态 (2) 过程,字符 (0,1,7) 道理都一样
s[x]=‘1’
s[x]=‘7’
s[x]=‘6’:对于字符 (6),由于不能包含 (2016),因此对于维持状态 (3(‘201’))->状态 (3(‘201’)),需删掉 (6),故 (mat[3][3]=1);对于状态 (4(‘2017’)),即若不删 (6),则会包含 (2016),因此要删去,即 (mat[4][4]=1),其余情况不做处理。
过程和 (AC) 自动机上跑 (DP) 类似,具体见代码。

代码:

#include<bits/stdc++.h>

using namespace std;
const int inf=0x3f3f3f3f;
const int N=2e5+5;
struct matrix
{
    int mat[5][5];
    void init()
    {
        for(int i=0;i<5;i++)
            for(int j=0;j<5;j++)
                mat[i][j]=inf;
    }
    matrix operator +(const matrix &b)const
    {
        matrix res;
        for(int i=0;i<5;i++)
        {
            for(int j=0;j<5;j++)
            {
                res.mat[i][j]=inf;
                for(int k=0;k<5;k++)
                    res.mat[i][j]=min(res.mat[i][j],mat[i][k]+b.mat[k][j]);
            }
        }
        return res;
    }
}tree[N<<2];
char ss[N];
void build(int l,int r,int rt)
{
    if(l==r)
    {//构造转移矩阵:
        tree[rt].init();
        for(int i=0;i<5;i++) tree[rt].mat[i][i]=0;
        if(ss[l]=='2') tree[rt].mat[0][0]=1,tree[rt].mat[0][1]=0;
        if(ss[l]=='0') tree[rt].mat[1][1]=1,tree[rt].mat[1][2]=0;
        if(ss[l]=='1') tree[rt].mat[2][2]=1,tree[rt].mat[2][3]=0;
        if(ss[l]=='7') tree[rt].mat[3][3]=1,tree[rt].mat[3][4]=0;
        if(ss[l]=='6') tree[rt].mat[4][4]=1,tree[rt].mat[3][3]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
matrix query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R)
        return tree[rt];
    int mid=(l+r)>>1;
    matrix res;
    res.init();
    for(int i=0;i<5;i++) res.mat[i][i]=0;
    if(L<=mid) res=res+query(l,mid,L,R,rt<<1);
    if(R>mid) res=res+query(mid+1,r,L,R,rt<<1|1);
    return res;
}
int main()
{
    int n,q,l,r;
    scanf("%d%d",&n,&q);
    scanf("%s",ss+1);
    build(1,n,1);
    while(q--)
    {
        scanf("%d%d",&l,&r);
        matrix ans=query(1,n,l,r,1);
        printf("%d
",ans.mat[0][4]==inf?-1:ans.mat[0][4]);
    }
    return 0;
}

参考博客

原文地址:https://www.cnblogs.com/1024-xzx/p/13261307.html