CF55D Beautiful numbers (数位DP)
2016-07-18 16:56
375 查看
codeforces 55D Beautiful numbers (数位DP)
原题地址:http://codeforces.com/problemset/problem/55/DD. Beautiful numbers
time limit per test4 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
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
题目的大意,就是要求出在区间 [ l,r ] 内,能被自身每一位数整除的数有多少个。
十分明显就是数位DP。
数位DP可以用记忆化搜索解决。
要点有两个,一个是DP数组的设计,一个是DFS的状态设计。
考虑到,要被自身每一位整除,那么,等价于能被每一位数的最小公倍数整除
而通过计算得知,1~9 的最小公倍数是2520 。
DP数组的设计,一般我们会想到DP [ 位数 ] [ 最小公倍数 ] [ mod 2520的余数 ]。
但是这样一来,我们就要将数组开到DP [ 20 ] [ 2520 ] [ 2520 ]。这样必然爆内存。
而考虑到,1~9的不同组合的最小公倍数也只有48个,我们可以将DP的第二维降到48。
DFS的状态可以设计为记录之前每一位的最小公倍数,和对2520的余数送给下一层。
因此得出DP [ 20 ] [ 48 ] [ 2520 ] ,分别代表数位,最小公倍数,对2520的余数。
long long int dfs( int pos , int lcm , int mod , bool limit),分别处理数位,最小公倍数,对2520的余数,界限。
那么我们就可以开工了~
穹妹镇贴~↓
数位DP有几个需要注意的地方,我因为这些细节吃了不少苦头(一整天都在找BUG):
在求余的时候,一般会这么求,比如
1234%18
因为要将数位分开处理,传递给下一位
所以我一开始用的是:
(((1*10^3 % 18 + 2*10^2) % 18 + 3*10^1) %18 + 4*10^0)%18
来求得最终的余数
然而这样免不了用pow函数。而pow函数的误差实在太大,导致在CF上,数据达到1000时,就出现了误差,第四组样例就已经挂了……
找了一晚上的BUG,检查各种细节。最后在翌日的早上,想到了pow的误差。
于是手写了一个pow函数,我想应该能好些,果然,第四组样例过了,挂在了第五组……
感觉整个人都不好了 T^T
手写的pow也有误差,那就只能不用了。
看了看大神的代码。大神没有用pow。
他是这么求余数的↓
1234%18
=(((1%18)*10 + 2)%18*10 + 3)%18 *10 + 4)%18
这样的话,每一次只需要乘以10。不需要用pow或者进行太多的累乘。
下面贴代码:#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int lcms[2523];
long long int dp[20][48][2524];
int digit[20];
int gcd(int a,int b)
{
if(b==0)return a;
else return gcd(b,a%b);
}
void init()
{
int num=0;
for(int i=1; i<=2520; i++)
if(2520%i==0)
{
lcms[i]=num++;
}
}
long long int dfs(int pos,int lcm,int mod,bool limit)
{
if(pos==-1)
{
return mod%lcm==0;
}
if(!limit&&dp[pos][lcms[lcm]][mod]!=-1)
{
return dp[pos][lcms[lcm]][mod];
}
long long int res=0;
int tmp=limit? digit[pos]:9;
for(int i=0; i<=tmp; i++)
{
int Nmod=(mod*10+i)%2520;
int Nlcm;
if(i==0||i==1)
{
Nlcm=lcm;
}
else
{
Nlcm=lcm/gcd(lcm,i)*i;
}
res+=dfs(pos-1,Nlcm,Nmod,limit&&i==tmp);
}
if(!limit)
{
dp[pos][lcms[lcm]][mod]=res;
}
return res;
}
long long int cal(long long int a)
{
int len=0;
while(a)
{
digit[len++]=a%10;
a=a/10;
}
return dfs(len-1,1,0,1);
}
int main()
{
long long int t,a,b;
scanf("%I64d",&t);
memset(dp,-1,sizeof(dp));
memset(lcms,-1,sizeof(lcms));
init();
while(t--)
{
scanf("%I64d%I64d",&a,&b);
printf("%I64d\n",cal(b)-cal(a-1));
}
return 0;
}
相关文章推荐
- 封装好的Folyd建图,C++源码
- matlab与c/c++混合编程
- matlab与c/c++混合编程——c/c++调用matlab
- TYVJ1193 括号序列解题报告
- TYVJ上一些DP的解题报告
- C/C++常用的调试宏
- VC内存泄露检查工具:VisualLeakDetector
- 内联函数总结
- usb体系结构
- VC结构体实现类似数组的下标操作符功能
- C/C++ 嵌套结构体动态内存管理实现
- C++调用gSoap编写的WEBSERVICE与C#.NET间接口自定义结构体不能重复使用
- 处理VC开发的webservice在C#.NET中中文乱码问题
- gcc使用入门
- C/C++ Linux 程序员必须了解的 10 个工具
- 递归遍历数组
- 用C/C++打印*号图案
- C++之四书五经(上)
- C++中const的使用
- *p++和*++p的区别