题意
求[X,Y]区间内能被其各位数(除0)均整除的数的个数。
CF 55D
有些时候因为问题的一些“整体性”而导致在按位统计的过程中不能顺便计算出某些量,所以只能在枚举到最后一位确定数字时才能计算相应的统计。
在本题中,我们无法在过程中确定到底有哪些数位,以及这个数本身,所以这些计算都要放在最后。所以首先我们需要参数num传递当前搜索确定的数字,以及判断该数字是否能被其数位整除。而判断各位整除只要数字整除各位数的最小公倍数lcm即可。
但我们随后发现了问题:各位数最小公倍数最大可能为lcm(1..9)=2520,而某个数最大为1018显然不能直接存。当然,我们可以用这个数%2520来代替数本身,但这样2520*2520*20的空间也不够。我们可以发现其实各位数的最小公倍数远没有2520个,而是2520的所有约数个数,不到50个。所以我们可以利用hash将空间缩至2520*50*20。这样,CF 55D便迎刃而解了(详见下面CF 55D代码)。
SPOJ JZPEXT
再来看CF 55D的升级版……两道都是7k+出的题只是这道更丧心病狂……全世界只有21个人AC……题目本身并没有变,只是限制了代码长度为1k!并且,数据组数增至104!
首先需要的是强劲的缩代码能力,这个不说了。我们发现,其实每个数2520的空间也浪费了,每次向下枚举一位都是(rem * 10 + i) mod 2520。。那么到最后一位的时候先给了之前的数2和5两个因子。。整个能不能多整除一个2和5只取决于i。。因此。。之前只需要记录模252的余数就可以了。。
最后需要注意的是需要把lcm和(rem*10+i)%252都预处理一下……
代码
【CF 55D】
[cpp]
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define MEM(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
int num[20], hash[2550];
LL dp[20][2550][55];
int gcd(int a, int b){
return b?gcd(b, a%b):a;
}
int cal_lcm(int a, int b){
return a/gcd(a, b)*b;
}
void init(){
MEM(dp, -1);
int n=2520, id=0;
for (int i = 1; i*i <= n; i ++){
if (n%i==0){
hash[i] = id++;
if (i != n/i){
hash[n/i] = id ++;
}
}
}
}
LL dfs(int pos, int mod, int lcm, bool limit){
if (pos == -1) return (mod%lcm==0);
if (!limit && ~dp[pos][mod][hash[lcm]]) return dp[pos][mod][hash[lcm]];
int end = limit?num[pos]:9; LL res = 0;
for (int i = 0; i <= end; i ++){
res += dfs(pos-1, (mod*10+i)%2520, i?cal_lcm(lcm, i):lcm, limit && (i==end));
}
return limit?res:dp[pos][mod][hash[lcm]]=res;
}
LL cal(LL x){
int i = 0;
while(x){
num[i++] = x%10;
x/=10;
}
return dfs(i-1, 0, 1, 1);
}
int main(){
init();
int t;
scanf("%d", &t);
while(t --){
LL l, r;
scanf("%lld %lld", &l, &r);
printf("%lld
", cal(r)-cal(l-1));
}
return 0;
}
[/cpp]
【SPOJ JZPEXT】
[cpp]
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int nu[20],h[2522],mo[255][10],mu[255][10],lc[2522][10];
LL f[20][55][255];
LL dfs(int p,int m,int l,bool li){
if(p==-1)return (m%l==0);
if(!li && ~f[p][h[l]][m]) return f[p][h[l]][m];
int e=li?nu[p]:9;LL r=0;
for (int i=0;i<=e;i++){r+=dfs(p-1,p?mo[m][i]:mu[m][i],lc[l][i],li&&(i==e));}
return li?r:f[p][h[l]][m]=r;
}
LL cal(LL x){
int i=-1;
while(x){nu[++i]=x%10;x/=10;} ++i;
return dfs(i-1,0,1,1);
}
int main(){
memset(f,-1,sizeof(f));
int i,j,id=0;
for (i=0;i<=252;i++)for (j=0;j<10;j++){mu[i][j]=i*10+j;mo[i][j]=mu[i][j]%252;}
for (i = 0; i <= 2520; i ++){
if (i>0 && 2520%i==0){h[i]=id++;}
for(j=0;j<10;j++)lc[i][j]=j?i*j/__gcd(i,j):i;
}
int t;
scanf("%d",&t);
while(t--){
LL l,r;
scanf("%lld%lld",&l,&r);
printf("%lld
",cal(r)-cal(l-1));
}
return 0;
}
[/cpp]