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\n",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;
}
posted @ 2020-10-21 11:50  lhm_liu  阅读(237)  评论(0编辑  收藏  举报