• 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;
    }
    
    

    参考博客

  • 相关阅读:
    来换网心得总结
    关于项目来换网
    数据库设计源代码
    自我介绍
    WC项目
    关于《现代软件工程》此书的疑问
    Swift基础语法(常量变量、数据类型、元组、可选、断言等)
    grunt快速学习
    Swift语言简介
    Swift简单入门教程:30分钟玩转Swift
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/13261307.html
Copyright © 2020-2023  润新知