CodeForces 55D Beautiful numbers (SPOJ JZPEXT 数位DP)
题意
求[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\n", 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\n",cal(r)-cal(l-1)); } return 0; } [/cpp]举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG