送分题
【问题描述】
每个人都会枚举,所以给大家一个送分题。
区间[L,R]内有多少个数字K满足:
K各个非0位都能被K整除?
例如,250的非0位是2,5,250/2=125,250/5=50。
【输入格式】
一行,两个正整数L,R。
【输出格式】
一个数字,表示答案的个数。
【输入样例】
12 15
【输出样例】
2
【数据范围与约定】
对于30%的数据,L,R<=10^5。
对于60%的数据,L,R<=9*10^9。
对于100%的数据,L,R<=10^18。
确实送了分,
前30%的数据一年之前,甚至于刚刚开始学oi的时候就可以写出来。
模拟,这个分解位数可以直接取模
60%的数据
万能的打表!
NOIP限制的提交源程序的体积在1MB以内,所以把10^10以内的数据全部打出来是不现实的,
然而我们可以在1s之内计算出10^6以内的数据(或者更多)
那么为何不隔10^6打一个表呢?
这个10^10的数据,我试过,在机子上跑大概25分钟就完了
用文件输出,每个数字中间各一个逗号,之后直接用到数组里面,
这样子60%的数据,输入之后先用除法和取模找到最近的那个表中的数据,然后直接进行有限次计算就OK了
分段打表!!!
索了这么多,但是由于Azuremayfly家里的电脑发热严重,所以表没打完
所以大家自行脑补一下这个程序吧,或者有时间到了配置I5的伟大TYZ机房,我再贡献出来这个表
NOIP考试的时候机子很好的
去年SN在全国闻名的西安友谊西路上最有名的学校考的,机子的内存,CPU什么的这个表还是可以搞定的
但是还是要写一段代码
/*
假装有打表的样子
假装watch[i]表示i*10^6的结果
ll r,ll l;
ll lans=watch[l/(1000000)];
ll rans=watch[r/1000000];
for(ll i= l/(1000000);i<=l;i++)
if(check(i)) lans++;
for(ll i= r/(1000000);i<=r;i++)
if(check(i)) rans++;
cout<<(rans-lans)<<endl;
return 0;
*/
来说满分做法
这是一个DP\记忆搜索
因为0123456789这几个的最小公倍数就2520,他们各个数的最小公倍数也不过50来种
所以就有搞头了
不要把数字看成一个大数字,就看成一个字符串或者一组数
在代码里面用了num数组,这样你就可以看到,巨大的数字也不过20来个
[L,R]范围内,有多少个数字,可以整除各个位上的数字的最小公倍数。
又因为,所有最小公倍数gcd肯定都是2520的约数,所以一个数字X%gcd一定等于X%2520%gcd。
dp[当前长度][对2520%的结果][当前最小公倍数(为了防止mle用了哈希)】
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
ll l,r;
ll ans;
ll t;
int a[25],ta;
bool check(ll x)
{
ta=0;
int nx=x;
while(x>0)
{
int nw=x%10;
x/=10;
a[ta]=nw;
ta++;
}
for(int i=0;i<ta;i++)
{
if(a[i]!=0)
{
if(nx%a[i]!=0)
return 0;
}
}
return 1;
}
ll dp[25][2525][52];
ll hashs[2525],top_hashs;
ll gcd(ll xxx, ll yyy)
{
if(!yyy)
return xxx;
else return gcd(yyy,xxx%yyy);
}
ll lcm(ll xxx,ll yyy)
{
ll res=xxx*yyy/gcd(xxx,yyy);
return res;
}
ll num[25];//stands for the number of each position of the ll number
ll top_num;
ll dfs(ll len_,ll mod_,ll lcm_,bool limit_)
{
if(len_==-1)
{
if(mod_%lcm_)
return 0;
else
return 1;
}
ll res=dp[len_][mod_][hashs[lcm_]];
if(limit_==0&&res!=-1) return res;
res=0;
ll lm;
if(limit_) lm=num[len_];else lm=9;
res+=dfs(len_-1,(mod_*10)%2520,lcm_,0);
for(int i=1;i<=lm;i++)
{
res+=dfs(len_-1,(mod_*10+i)%2520,lcm(lcm_,i),limit_&&i==lm);
}
if(!limit_)
dp[len_][mod_][hashs[lcm_]]=res;
return res;
}
ll solve(ll obj)
{
memset(num,0,sizeof(num));
top_num=0;
while(obj>0)
{
ll nw=obj%10;
obj/=10;
num[top_num]=nw,top_num++;
//works like the stack
}
return dfs(top_num-1,0,1,1);
}
int main()
{
cin>>l>>r;
if(l<=100000&&r<=100000)
{
for(ll i=l;i<=r;i++)
{
if(check(i))
ans++;
}
cout<<ans<<endl;
return 0;
}
else
{
memset(dp,-1,sizeof dp);
int cnt = 0;
for (int i = 1; i <= 2520; i++)
if (2520 % i == 0) hashs[i] = cnt++;
//用50以内的数字来表示 最小公倍数,方便数位dp;
ll ans=solve(r)-solve(l-1);
cout<<ans<<endl;
return 0;
}
}