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\times 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\n",ans.mat[0][4]==inf?-1:ans.mat[0][4]);
    }
    return 0;
}

参考博客

posted @ 2020-07-07 15:54  xzx9  阅读(224)  评论(0编辑  收藏  举报