[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 }

 

posted @ 2017-10-20 14:02  skylee03  阅读(1234)  评论(0编辑  收藏  举报