CodeForces - 1036C Classy Numbers(数位dp)

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出现的次数

希望能够帮助到大家!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值