1 条题解

  • 0
    @ 2025-8-24 21:30:49

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar SNiFe
    但行好事,莫问前程

    搬运于2025-08-24 21:30:49,当前版本为作者最后更新于2017-10-25 17:49:29,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    #此题是数位DP,没学过数位DP的,这可以是一道很经典的入门题目

    1. 本题是一道数位DP,首先我们可以只考虑设计算法求[1,x]这个区间内符合条件的数的个数即可。因为[x,y]这个区间内的个数实际上是[1,y]区间内的个数减去[1,x-1]区间内的个数。(注意要把0特殊出来考虑)

    **2.**之后我们可以枚举支点的位置,对于每个满足条件的数,它所对应的支点是唯一的,原因是如果将支点右移,左边减去右边的差将严格单调增加。state表示力矩和(支点左边加支点右边),所以当state<0时,当前这个数不满足以i为支点成为杠杆数的情况,返回0。但当state==0时并不能就ans++了,因为当前枚举的位置可能还没枚举完。

    **3.**枚举好支点,问题就转化为:求[1,x]中,以第i位为支点的杠杆数的个数。

    ==》 我们就可以用数位DP解决此问题。

    注意:注意当力矩为负时,就要返回,否则会出现下标为负。

    (数位DP可以从后往前推,也可以用记忆化搜索。我比较喜欢记忆化搜索,比较有套路。)

    #代码附上:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long
    using namespace std;
    const int N=20;
    int a[N];//储存每一位的大小 
    LL dp[N][N][2500],l,r;//dp[i][j][k]表示考虑i位数字,支点为j,力矩和为k 
    LL dfs(int pos,int point,int state,bool limit)//pos是几位数字;ponit是支点;state是力矩;limit表示当前这一位有无大小限制,防止枚举超过上限 
    {
        if(pos==0)return state==0;//判断是否合法 
        if(state<0)return 0;//当前力矩为负
        if(!limit&&dp[pos][point][state]!=-1)return dp[pos][point][state];
        int up=limit?a[pos]:9;//数位上限 
        LL tmp=0;
        for(int i=0;i<=up;i++)tmp+=dfs(pos-1,point,state+i*(pos-point),limit&&(i==up));
        if(!limit)dp[pos][point][state]=tmp;
        return tmp;
    }
    LL solve(LL x)
    {
        int len=0;
        while(x)
        {
            a[++len]=x%10;
            x/=10;
        }
        LL ans=0;
        for(int i=1;i<=len;i++)ans+=dfs(len,i,0,1);
        return ans-len+1;//每次dfs都会重复搜索到00000……的情况这里减去重复数 
    }
    int main()
    {
        scanf("%lld %lld",&l,&r);
        memset(dp,-1,sizeof(dp));
        printf("%lld",solve(r)-solve(l-1));
        return 0;
    }
    
    • 1

    信息

    ID
    801
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者