題目大意:
問區間[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 }