zoukankan      html  css  js  c++  java
  • CF1110H Modest Substrings

    暴力的做法就是将区间 ([l,r]) 内的数都插到 (AC) 自动机中,然后 (DP) 确定字符串。

    需要进一步优化,先转化为 (leqslant r) 的贡献减 (leqslant l-1) 的贡献,那么接下来就只用考虑形如 (leqslant a) 的限制了。(AC) 自动机中只插入 (l)(r)

    考虑一个数 (x)(a) 的关系:

    (|x| < |a|),则其无前导零即可产生贡献。在每个节点统计长度 (<|a|) 的子串即可。

    (|x| = |a|),则当 (lcp(x,a)) 的下个位置 (i) 满足 (x_i < a_i) 时,其可以产生贡献。在每个前缀节点走 (< a_i) 的出边,同时还需限制继续匹配的长度。

    (x=a),直接在 (a) 的终止节点上统计贡献即可。

    这样可以处理出 (sum_{p,c,i}),其表示从节点 (p),往出边 (c)(i) 步所能到达的节点的贡献和。因为 (fail_p)(p) 的一段后缀,所以 (fail_p) 能获得的贡献,(p) 也一定能获得,那么 (p) 需要从 (fail_p) 继承贡献。

    因为要字典序最小,所以要倒着 (DP)。设 (f_{p,i}) 为到节点 (p) 已经走了 (i) 步的最大贡献和,得转移:

    [large f_{p,i}=max{ f_{ch_{p,c},i+1}+sum_{j=1}^{n-i} sum_{p,c,j} } ]

    前缀和后即可快速转移。

    #include<bits/stdc++.h>
    #define maxn 2010
    #define inf 2000000000
    using namespace std;
    template<typename T> inline void read(T &x)
    {
        x=0;char c=getchar();bool flag=false;
        while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        if(flag)x=-x;
    }
    int len1,len2,n,cnt,root,tot;
    int ch[maxn][10],fail[maxn],sum[maxn][10][maxn],f[maxn][maxn];
    char L[maxn],R[maxn];
    void insert(char *s,int len)
    {
        if(s[1]=='0') return;
        for(int i=1,p=root;i<=len;++i)
        {
            int c=s[i]-'0';
            if(!ch[p][c]) ch[p][c]=++tot;
            p=ch[p][c];
        }
    }
    void build()
    {
        queue<int> q;
        for(int i=0;i<=9;++i)
            if(ch[root][i])
                q.push(ch[root][i]);
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            for(int i=0;i<=9;++i)
            {
                int y=ch[x][i];
                if(y) fail[y]=ch[fail[x]][i],q.push(y);
                else ch[x][i]=ch[fail[x]][i];
            }
        }
    }
    void update(char *s,int len,int v)
    {
        if(s[1]=='0') return;
        for(int i=1,p=root;i<=len;++i)
        {
            int c=s[i]-'0';
            for(int j=p==root;j<c;++j) sum[p][j][len-i+1]+=v;
            if(i==len) sum[p][c][1]+=v;
            p=ch[p][c];
        }
    }
    int main()
    {
        scanf("%s%s",L+1,R+1),read(n);
        len1=cnt=strlen(L+1),len2=strlen(R+1),L[cnt]--;
        while(L[cnt]-'0'==-1) L[cnt]=9+'0',L[--cnt]--;
        if(L[1]=='0')
        {
            len1--;
            for(int i=1;i<=len1;++i) L[i]=L[i+1];
        }
        insert(L,len1),insert(R,len2);
        build(),update(L,len1,-1),update(R,len2,1);
        for(int p=1;p<=tot;++p)
            for(int c=0;c<=9;++c)
                for(int i=1;i<=n;++i)
                    sum[p][c][i]+=sum[fail[p]][c][i];
        for(int p=root;p<=tot;++p)
        {
            for(int c=1;c<=9;++c)
            {
                for(int i=1;i<len1;++i) sum[p][c][i]--;
                for(int i=1;i<len2;++i) sum[p][c][i]++;
            }
        }
        for(int p=root;p<=tot;++p)
            for(int c=0;c<=9;++c)
                for(int i=1;i<=n;++i)
                    sum[p][c][i]+=sum[p][c][i-1];
        for(int i=0;i<n;++i)
            for(int p=root;p<=tot;++p)
                f[p][i]=-inf;
        for(int i=n-1;i>=0;--i)
            for(int p=root;p<=tot;++p)
                for(int c=0;c<=9;++c)
                    f[p][i]=max(f[p][i],f[ch[p][c]][i+1]+sum[p][c][n-i]);
        printf("%d
    ",f[root][0]);
        for(int i=0,p=root;i<n;++i)
        {
            for(int c=0;c<=9;++c)
            {
                if(f[p][i]==f[ch[p][c]][i+1]+sum[p][c][n-i])
                {
                    printf("%d",c),p=ch[p][c];
                    break;
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    (15)树莓派系统安装和备份
    (0-0) 树莓派学习资料
    (14)树莓派
    (0-1) 树莓派常用软件及服务
    (13)flask搭建服务器
    (12)树莓派串口通信
    OpenCV 学习笔记(0)两幅图像标定配准
    OpenCV 学习笔记(9)RGB转换成灰度图像的一个常用公式Gray = R*0.299 + G*0.587 + B*0.114
    OpenCV 学习笔记(8)彩色图像RGB通道的分离、合并与显示
    Arduino OV7670 live image over USB to PC
  • 原文地址:https://www.cnblogs.com/lhm-/p/13851534.html
Copyright © 2011-2022 走看看