[CodeForces-55D]Beautiful Numbers 数位dp+离散化 可真奇妙

这有一堆美丽 数

Beautiful number

Volodya is an odd boy and his taste is strange as well. It seems to him that a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits. We will not argue with this and just count the quantity of beautiful numbers in given ranges.

Input

The first line of the input contains the number of cases t (1 ≤ t ≤ 10). Each of the next t lines contains two natural numbers li and ri (1 ≤ li ≤ ri ≤ 9 ·1018).

Please, do not use %lld specificator to read or write 64-bit integers in C++. It is preffered to use cin (also you may use %I64d).

Output

Output should contain t numbers — answers to the queries, one number per line — quantities of beautiful numbers in given intervals (from li to ri, inclusively).

Examples

Input

1
1 9

Output

9

Input

1
12 15

Output

2

有一种美丽的数,他的特征是他的每一个非零组成数字都能够整除他本身,给你一个范围,让你求出这个范围内有多少个这样的beautiful number ?

这是一个多组样例,而且给的数的范围及其之大,~9e18,这谁能降得住啊,肯定又有一些奇奇怪怪的技巧,首先这是一个数位dp题,这就将时间复杂度降了下来,但是你要开一个及其之大的dp数组来存结果,这又是一个谜

咱们先普及一下这道题会用到的背景知识,若一个数能整除它的所有的非零组成数字,那么相当于它能整除所有非零组成数字的的最小公倍数。

其实 1~9的最小公倍数是2520,也就是说所有的组成数字的最小公倍数的最大值不会大于2520.

所以把这个数%2520,得到的数字如果还可以整除数字的lcm的话,那么这个数还是美丽数,,至于证明过程还真不会,但是举两个例子就可以理解的

我们可以设一个dp数组,dp[25][2525][2525],分别表示pos当前在第几位,pmod前面的数字对2520的取模,plcm前面的数的lcm

但是这么大的数组根本没法开啊,可定还需要进一步优化

身边的大佬悄咪咪地tell me 其实枚举1~9的有效最小公倍数只有48个。。。大佬就是大佬,厉害,Orz

这样我们就可以使用离散化的思想了,即记录一下可以被2520整除的数字,留下他的id,这样碰见的时候直接对应一下就OJBK喽

剩下的就是板子了,需要注意的只有就是dfs的内部写法还有要使用long long 不然会WA的很惨

下面是我的AC代码:

//ll dfs(ll pos,ll pmod,ll plcm,ll limit){//pos,前面的数对2520的mod,前面数的lcm,limit  
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const ll inf=0x3f3f3f3f;
const ll lcmm=2520;
ll dp[25][lcmm+10][50];//pos,pmod,id[plcm]
ll id[lcmm+10];//记录plcm的id 
ll a[25];

ll gcd(ll a,ll b){
	if(b==0)return a;
	return gcd(b,a%b);
}
ll lcm(ll a,ll b){
	return a/gcd(a,b)*b;
}

void init(){
	ll cnt=0;
	for(ll i=1;i<=lcmm;i++)
		if(lcmm%i==0)
			id[i]=cnt++;//离散化  记录id 
	mem(dp,-1);
}

ll dfs(ll pos,ll pmod,ll plcm,ll limit){//pos,前面的数对2520的mod,前面数的lcm,limit 
	if(pos<0)return pmod%plcm==0;
	if(!limit&&~dp[pos][pmod][id[plcm]])
		return dp[pos][pmod][id[plcm]];
	ll res=0;
	ll up=limit?a[pos]:9;
	for(ll i=0;i<=up;i++){
		ll nmod=(pmod*10+i)%lcmm;//前面累积的mod 
		ll nlcm=plcm;//注意非零的时候 
		if(i) nlcm=lcm(plcm,i);//新的 lcm 
		
		res+=dfs(pos-1,nmod,nlcm,i==a[pos]&&limit);
	}
	if(!limit) dp[pos][pmod][id[plcm]]=res;
	return res;
}

ll solve(ll x){
	ll cnt=0;
	while(x){
		a[cnt++]=x%10;
		x/=10;
	}
	return dfs(cnt-1,0,1,1);
}

int main()
{
	ll t;
	cin>>t;
	init();//又忘了写了。。。
	while(t--){
		ll x,y;
		cin>>x>>y;
		ll res=solve(y)-solve(x-1);	
		cout<<res<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值