[CodeForces-55D]Beautiful Numbers
题目大意:
问区间[l,r]内有多少正整数能被其各个数位上的所有数字整除。
思路:
数位DP。
能被所有数位整除相当于能被所有数位的LCM整除。
而1..9的LCM为2520。
f[i][j][k][flag]表示DP到第i位,当前构成的数字在%2520意义下为j,当前各个数位的LCM为k,flag表示当前数是否为边界。
第三维的k显然不会完全用到,事实上只会有48个可能的值,因此可以离散化。
枚举当前数位p,设边界数的当前数位为cur,则转移方程为:
f[i-1][(j*10+p)%SUM][mult[k][p-1]][false]+=f[i][j][k][false];
f[i-1][(j*10+p)%SUM][mult[k][p-1]][false]+=f[i][j][k][true];(p<cur)
f[i-1][(j*10+p)%SUM][mult[k][p-1]][true]+=f[i][j][k][true];(p==cur)
压一下内存,可以用滚动数组。
然而交上去发现会在第11个点TLE。
试了各种奇怪的方法,并没有什么用。
还发现不开优化跑40s的数据,开了O1只需要跑3s。
最后开性能分析,发现是在lcm的地方花了很多时间。
于是先打表出所有的LCM,然后就过了。
(然而51Nod上是10000组数据,不用记忆化搜索根本过不了)
1 #include<cstdio> 2 #include<algorithm> 3 using std::__gcd; 4 typedef unsigned long long qword; 5 inline qword getint() { 6 register char ch; 7 while(!__builtin_isdigit(ch=getchar())); 8 register qword x=ch^'0'; 9 while(__builtin_isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 10 return x; 11 } 12 const int SUM=2520,LCM=48; 13 const qword pow[]={1ull,10ull,100ull,1000ull,10000ull,100000ull,1000000ull,10000000ull,100000000ull,1000000000ull,10000000000ull,100000000000ull,1000000000000ull,10000000000000ull,100000000000000ull,1000000000000000ull,10000000000000000ull,100000000000000000ull,1000000000000000000ull,10000000000000000000ull}; 14 int id[SUM+1]; 15 const int mul[]={1,2,3,4,5,6,7,8,9,10,12,14,15,18,20,21,24,28,30,35,36,40,42,45,56,60,63,70,72,84,90,105,120,126,140,168,180,210,252,280,315,360,420,504,630,840,1260,2520}; 16 inline int lcm(const int &a,const int &b) { 17 return a/__gcd(a,b)*b; 18 } 19 int mult[LCM][LCM]; 20 inline void init() { 21 for(register int i=0;i<LCM;i++) { 22 id[mul[i]]=i; 23 } 24 for(register int i=0;i<LCM;i++) { 25 for(register int j=0;j<LCM;j++) { 26 mult[i][j]=id[lcm(mul[i],mul[j])]; 27 } 28 } 29 } 30 qword f[2][SUM][LCM][2]; 31 inline qword calc(const qword &n) { 32 if(!n) return 1; 33 const int len=__builtin_log10(n)+1; 34 __builtin_memset(f[len&1],0,sizeof f[len&1]); 35 f[len&1][0][0][true]=1; 36 for(register int i=len;i;i--) { 37 __builtin_memset(f[!(i&1)],0,sizeof f[!(i&1)]); 38 const int cur=n%pow[i]/pow[i-1]; 39 for(register int j=0;j<SUM;j++) { 40 for(register int k=0;k<LCM;k++) { 41 if(!f[i&1][j][k][true]&&!f[i&1][j][k][false]) continue; 42 f[!(i&1)][(((j<<2)+j)<<1)%SUM][k][false]+=f[i&1][j][k][false]; 43 f[!(i&1)][(((j<<2)+j)<<1)%SUM][k][!cur]+=f[i&1][j][k][true]; 44 for(register int p=1;p<10;p++) { 45 f[!(i&1)][((((j<<2)+j)<<1)+p)%SUM][mult[k][p-1]][false]+=f[i&1][j][k][false]; 46 if(p<cur) f[!(i&1)][((((j<<2)+j)<<1)+p)%SUM][mult[k][p-1]][false]+=f[i&1][j][k][true]; 47 if(p==cur) f[!(i&1)][((((j<<2)+j)<<1)+p)%SUM][mult[k][p-1]][true]+=f[i&1][j][k][true]; 48 } 49 } 50 } 51 } 52 qword ret=0; 53 for(register int j=0;j<SUM;j++) { 54 for(register int k=0;k<LCM;k++) { 55 if(!(j%mul[k])) ret+=f[0][j][k][true]+f[0][j][k][false]; 56 } 57 } 58 return ret; 59 } 60 int main() { 61 init(); 62 for(register int T=getint();T;T--) { 63 const qword l=getint(),r=getint(); 64 __builtin_printf("%I64u\n",calc(r)-calc(l-1)); 65 } 66 return 0; 67 }