CF1036C Classy Numbers 题解

题意

问从 \(l\)\(r\) 有多少个数的十进制表示有不超过 \(3\) 个非零位。

分析

一道数位 dp 的模板题。从后向前枚举每一位,如果这一位放 \(0\) ,就从前一位、非零位个数相同转移,否则从前一位,非零位个数 \(+1\) 转移。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a));
#define elif else if
using namespace std;
typedef long long ll;
ll T,l,r,dp[27][7];
vector<int>a;
ll dfs(int pos,int cnt,bool limit)
{
    if(pos==-1) return 1; //枚举到头了
    if(!limit&&dp[pos][cnt]!=-1) return dp[pos][cnt]; //如果之前已经得出答案了
    ll ans=0;
    int up=limit?a[pos]:9; //如果之前放满了,这一位就只能枚举到a[pos],否则能枚举到9
    for(int i=0;i<=up;i++)
    {
        if(i==0)
        {
            ans+=dfs(pos-1,cnt,limit&&i==a[pos]); //如果这一位放0,就从前一位、非零位个数相同转移
        }
        elif(cnt<3) ans+=dfs(pos-1,cnt+1,limit&&i==a[pos]); //如果不放0,就从前一位,非零位个数+1转移
    }
    return limit?ans:dp[pos][cnt]=ans; 
}
ll solve(ll x)
{
    a.clear();
    int pos=0;
    while(x) //拆位
    {
        a.push_back(x%10);
        x/=10;
        pos++;
    }
    return dfs(a.size()-1,0,true); //从后向前枚举
}
int main()
{
    scanf("%lld",&T);
    mem(dp,-1); //赋一下初始值,方便记忆化
    while(T--)
    {
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}
posted @ 2022-02-18 11:12  l_x_y  阅读(20)  评论(0编辑  收藏  举报