【CF1036C】 Classy Numbers

题目

题意翻译
定义一个数字是“好数”,当且仅当它的十进制表示下有不超过33个数字1 \sim 91∼9
举个例子:4,200000,102034,200000,10203是“好数”,然而4231,102306,72774200004231,102306,7277420000不是

给定[l,r][l,r],问有多少个xx使得l \le x \le rl≤x≤r,且xx是“好数”

一共有T(1 \le T \le 10^{4})T(1≤T≤10
4
)组数据,对于每次的询问,输出一行一个整数表示答案

1 \le l_i \le r_i \le 10^{18}1≤l
i
​ ≤r
i
​ ≤10
18

题目描述
Let’s call some positive integer classy if its decimal representation contains no more than 3 3 non-zero digits. For example, numbers 4 4 , 200000 200000 , 10203 10203 are classy and numbers 4231 4231 , 102306 102306 , 7277420000 7277420000 are not.

You are given a segment [L; R] [L;R] . Count the number of classy integers x x such that L \le x \le R L≤x≤R .

Each testcase contains several segments, for each of them you are required to solve the problem separately.

输入输出格式
输入格式:
The first line contains a single integer T T ( 1 \le T \le 10^4 1≤T≤10
4
) — the number of segments in a testcase.

Each of the next T T lines contains two integers L_i L
i
​ and R_i R
i
​ ( 1 \le L_i \le R_i \le 10^{18} 1≤L
i
​ ≤R
i
​ ≤10
18
).

输出格式:
Print T T lines — the i i -th line should contain the number of classy integers on a segment [L_i; R_i] [L
i
​ ;R
i
​ ] .

输入输出样例
输入样例#1: 复制
4
1 1000
1024 1024
65536 65536
999999 1000001
输出样例#1: 复制
1000
1
0
2

思路

一般地,数位dp可以用来解决下面的问题: 给定一个结果为bool类型的函数f(x),求[L,R]内有多少个数满足f(x)是真。其中一般L,R<=1e7L,R<=1e7或更大,且f(x)与数位相关。

本题中,用dp[i][j]代表考虑前i个数位,当前i位中有j个非零数位时,有多少个数满足条件。

这样我们就很容易处理[1,pow(10,t)]中的答案。

现在假设我们需要求[1,3812]的答案。当我们从第一位开始dfs时,从0遍历到3(最高位最大为3),如果第一位是0或1或2,则后面的数据无需考虑限制,都可以从0遍历到9。如果第一位是3,则第二位最大是8。我们使用数组上界变量代表这个限制,如果变量为0代表这一位无需考虑限制,否则代表这一位需要考虑限制。

代码

#include <iostream>
#include <cstring>
using namespace std;

typedef long long ll;

ll a[20]; 
ll dp[20][5];

ll dfs(ll pos, ll st, bool limit) 
{
    if(pos==-1) return 1; 
    if(!limit && dp[pos][st]!=-1) return dp[pos][st];
    ll up=limit?a[pos]:9; 
    ll ans=0;
    for(ll i=0;i<=up;i++)
    {
        if(i==0) ans+=dfs(pos-1,st,limit&&a[pos]==i);
        else if(st!=3) ans+=dfs(pos-1,st+1,limit&&a[pos]==i);
    }
    if(!limit) dp[pos][st]=ans;
    return ans;
}

ll solve(ll x) 
{
    ll tot=0;
    //处理出每一位
    while(x>0)
    {
        a[tot++]=x%10;
        x/=10;
    }
    return dfs(tot-1,0,true);
}

int main()
{
    memset(dp,-1,sizeof(dp));
    ll T; 
    cin>>T;
    while(T--)
    {
        ll l,r;
        cin>>l>>r;
        cout<<solve(r)-solve(l-1)<<endl;

    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值