Let's call some positive integer classy if its decimal representation contains no more than 33 non-zero digits. For example, numbers 4, 200000, 10203 are classy and numbers 4231, 102306, 7277420000 are not.
You are given a segment [L;R]. Count the number of classy integers xx such that L≤x≤R.
Each testcase contains several segments, for each of them you are required to solve the problem separately.
Input
The first line contains a single integer T (1≤T≤10^4) — the number of segments in a testcase.
Each of the next T lines contains two integers LiLi and RiRi (1≤Li≤Ri≤10^18).
Output
Print T lines — the i-th line should contain the number of classy integers on a segment [Li;Ri]
Example
Input
4 1 1000 1024 1024 65536 65536 999999 1000001
Output
1000 1 0 2
先来简单介绍一下题意,题目意思就是说给你一个l和r,让你求出在闭区间【l,r】中满足题目条件的数的个数,x满足题目条件当且仅当x的十进制表示中各个位置上出现数字1~9的个数小于等于3
一看到区间就知道,这又是一个差分解决的问题,关键是如何找出0~n中有多少符合题意的数,直接找可能不太好找,但我们可以想他的反面,也就是寻找出现数字1~9的个数大于3的数目,用n-这个数不就是答案了吗?
下面看代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
int a[300];
ll dp[30][20][2];
ll dfs(int pos,int cnt,int flag1,int flag2)//cnt用于记录到目前为止数字1~9出现的次数
{
if(pos==0) return flag1;//函数递归出口
if(dp[pos][cnt][flag1]!=-1&&flag2)//只有保证dp数组被更新过以及当前位的数字可以任选才能够使用记忆化数组储存的值
return dp[pos][cnt][flag1];
ll ans=0;//记录第pos位取不同值时的方案数之和,更新dp数组
int x=flag2?9:a[pos];//确定当前可以枚举的上限
for(int i=0;i<=x;i++)
ans+=dfs(pos-1,cnt+(i!=0),(cnt+(i!=0)>3)||flag1,i<x||flag2);//枚举下一位
//如果在某次函数调用后flag1变为1,说明到当前位置已经满足题目中所给条件,则之后一定会一直满足条件
//如果在某次函数调用后flag2变为1,说明到当前位置已经可以任选,则之后一定可以任选(比如两个数字比大小,当一个数字的高位大于另一个数字,则不管低位是什么,高位大的一定大)
if(flag2) dp[pos][cnt][flag1]=ans;//只有保证当前位置任选才能够更新记忆化数组储存的值
return ans;
}
ll solve(ll x)
{
int tt=0;
memset(dp,-1,sizeof dp);//每进行一次函数调用都要初始化一次
do
{
a[++tt]=x%10;
x/=10;
}while(x);
return dfs(tt,0,0,0);//函数调用,从高位往低位遍历
}
int main()
{
int T;
ll n,m;
cin>>T;
while(T--)
{
scanf("%lld%lld",&n,&m);
printf("%lld\n",(m-solve(m))-(n-1-solve(n-1)));
}
return 0;
}
这道题目给了我两个启发
第一个就是当我们正面思考问题不是很好处理的话不妨反过来试试,就比如这道题,直接找各个位上出现数字1~9的次数小于等于3的不太好找,就找出现次数大于3的情况,用总数一减就是答案。
第二个启发就是我们需要根据不同的题目在搜索过程中加入一些可以帮助我们确定当前状态的变量,从而方便更新flag,就比如本题加入cnt来记录当前位置之前数位上1~9出现的次数
希望能够帮助到大家!