• 洛谷P3413 SAC#1


    题目

    https://www.luogu.com.cn/problem/P3413

    思路

    不要被“回文”这个东西吓到了。考虑一个任意长度(geq 2)的回文串,它必然包含一个长度为3或长度为2的回文子串。也就是说,只要串中存在(S_{i})满足(S_{i}==S_{i-1})(S_{i}==S_{i-2}),那么这个数就是萌数.

    接下来就是数位DP的常规套路了,设(f(x))(1)(x)中萌数的个数,最终答案就是(f(r)-f(l-1))。考虑到本题(l)(r)只能以字符串形式存储,为了避免高精度减法,我们把答案换成(f(r)-f(l)),如果(l)自身是萌数,则答案加一。(这个直接暴力检验)。

    最重要的就是数位DP的主过程了。

    从高位向低位处理。

    ll dfs(char *x,int digit,int limit,int k/*该数位前有多少个非前导零的位*/)
    

    我的dfs过程是这么写的,其中(x)是字符数组,表示传进去的(l)(r),digit表示当前处理的是第几位(为了方便计算,这里指权值为(10^{digit})的那一位),(limit)表示之前取的数是否是上限(这一点可能说的不是很清楚,可以看其他大佬的数位DP博客,(limit)基本都有相同的含义),(k)表示我们当前构造的萌数已经有了多少位(其实就是用来特判开头两个数字的,因为他们的(digit-2)不存在)。

    设当前位为(S_{digit}),那么我们考虑它能否与(S_{digit-1})(S_{digit-2})相同,针对(limit)(k)的不同取值做不同的决策(这部分详见注释)。

    分类讨论写的比较丑,大佬轻喷QWQ。

    代码

    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define mod (int)(1e9+7)
    #define ll long long
    #define maxn 1010
    using namespace std;
    char l[maxn],r[maxn];
    ll pow_10[maxn],suf[maxn];//预处理10的次幂及l和r的后缀
    ll dp[maxn][10][10];
    int LEN;
    ll dfs(char *x,int digit,int limit,int k/*该数位前有多少个非前导零的位*/){
        ll sum=0;
        if(digit<0) return 0;
        if(k==0){
            if(limit){//有限制
                sum+=(x[digit]-'0'-1)*dfs(x,digit-1,0,1)%mod;sum%=mod;//当前位不取0也不取到x[i],之后的选择不受限
                sum+=dfs(x,digit-1,1,1);sum%=mod;//当前位取到x[i],这将使之后的选择仍然受限
                sum+=dfs(x,digit-1,0,0);sum%=mod;//当前位空出,继续填0,之后的选择不受限
            }
            else{//无限制
                sum+=9*dfs(x,digit-1,0,1)%mod;sum%=mod;//当前填1~9,由于之前的填法已经使构造的值必然小于x,所以之后当然也没有限制
                sum+=dfs(x,digit-1,0,0);sum%=mod;//当前填0
            }
        }
        else if(k==1){
            if(limit){//有限制
                if(x[digit]>x[digit+1]){//x的当前位大于前一位,这使我们可以令这一位=x[digit+1],从而满足萌数条件
                    sum+=(x[digit]-'0'-1)*dfs(x,digit-1,0,2)%mod;sum%=mod;//平凡值,不满足萌数也不是上限,取法有(x[digit]-'0'-1)种
                    sum+=dfs(x,digit-1,1,2);sum%=mod;//取上限,之后受限
                    sum+=pow_10[digit];sum%=mod;//取x[digit+1],使满足萌数条件,后面随便取,总计pow_10[digit](后面digit个位置每个有10种取法)
                }
                else if(x[digit]==x[digit+1]){//取上限时正好满足萌数条件,与大于的情况有一些微妙不同
                    sum+=(x[digit]-'0')*dfs(x,digit-1,0,2)%mod;sum%=mod;
                    sum+=suf[digit]+1;sum%=mod;//这里,虽然已经满足萌数条件,但此时若任意取有可能导致超出上界,所以不是10的次幂而是x的后缀
                }
                else{//该位已经无法满足萌数条件了
                    sum+=(x[digit]-'0')*dfs(x,digit-1,0,2)%mod;sum%=mod;
                    sum+=dfs(x,digit-1,1,2);sum%=mod;
                }
            }
            else{//无限制
                sum+=9*dfs(x,digit-1,0,2)%mod;sum%=mod;//取不等于前一位的值,有⑨种取法
                sum+=pow_10[digit];sum%=mod;//取等于前一位的数字,满足萌数条件
            }
        }
        else{//k>=2,与k==1相比无非多了一个与digit-2位的比较,只不过计数麻烦一点,注意细节
            if(limit){
                int c=0;
                if(x[digit+1]<x[digit]){
                    c++;
                    sum+=pow_10[digit];sum%=mod;
                }
                if(x[digit+1]==x[digit]){
                    sum+=suf[digit]+1;sum%=mod;
                }
                if(x[digit+2]<x[digit]){
                    c++;
                    sum+=pow_10[digit];sum%=mod;
                }
                if(x[digit+2]==x[digit]){
                    sum+=suf[digit]+1;sum%=mod;
                }
                sum+=(x[digit]-'0'-c)*dfs(x,digit-1,0,k+1)%mod;sum%=mod;
                if(x[digit]!=x[digit+1]&&x[digit]!=x[digit+2]){
                    sum+=dfs(x,digit-1,1,k+1);sum%=mod;
                }
            }
            else{
                sum+=8*dfs(x,digit-1,0,k+1)%mod;sum%=mod;//与之前选择填上digit+1位,digit+2位均不同
                sum+=2*pow_10[digit]%mod;sum%=mod;//与其中之一相同
                //假设我们正在构造的萌数候选用y数组表示,值得注意的是y[digit+1]和y[digit+2]保证不同,否则我们早就因为满足萌数条件而直接计算出值了,不会走到这一步。
                //所以这里的8和2是确定的
            }
        }
        return sum;
    }
    ll f(char *s){
        LEN=strlen(s);
        for(int i=1;i<LEN;i++){
            suf[i]=suf[i-1]+(s[i-1]-'0')*pow_10[i-1];
        }//简简单单的预处理
        return dfs(s,LEN-1,1,0);
    }
    void rev(char *s){
        int n=strlen(s);
        for(int i=0;2*i<n-1;++i) swap(s[i],s[n-1-i]);
    }
    int main(){
        int i,flag=0,n;
        ll ans;
        scanf("%s%s",l,r);
        rev(l);rev(r);
        pow_10[0]=1;
        for(i=1;i<=1000;i++)
            pow_10[i]=pow_10[i-1]*10%mod;
        ans=f(r)-f(l);
        n=strlen(l);
        if(l[0]==l[1]) flag=1;
        for(i=2;i<n;i++){
            if(l[i]==l[i-1]||l[i]==l[i-2]){
                flag=1;
                break;
            }
        }
        if(flag) ans++;
        ans=(ans+mod)%mod;
        // printf("%lld %lld
    ",f(l),f(r));
        printf("%lld",ans);
        // system("pause");
        return 0;
    }
  • 相关阅读:
    python 约束与异常处理
    ActiveMQ
    SpringMVC项目,启动项目怎么总是报找不到log4j.properties文件
    java 字符串处理
    java面向对象学习笔记
    JSONArray遍历
    get/post方式调用http接口
    IO流认识
    Apache Mina 入门实例
    “wsimport -keep ”生成客户端报错“Use of SOAP Encoding is not supported.”
  • 原文地址:https://www.cnblogs.com/landmine-sweeper/p/15404572.html
Copyright © 2020-2023  润新知