非常神的数位dp,我调了几乎一天
首先和bzoj3131类似,乘积是可以预处理出来的,注意这里乘积有一个表示的技巧
因为这里质因数只有2,3,5,7,所以我们可以表示成2^a*3^b*5^c*7^d,也就是v[a,b,c,d]这个数组
这样也非常方便进行除法,乘法的状态转移,转移和要维护的东西比较多,我就直接在程序里注释了
这里维护转移的核心思想是算每一位对编号和的贡献,乘积为0和非0分开计算
1 const mo=20120427; 2 lim=1000000000000000000; 3 q:array[1..9,1..4] of longint=((0,0,0,0),(1,0,0,0),(0,1,0,0),(2,0,0,0),(0,0,1,0), 4 (1,1,0,0),(0,0,0,1),(3,0,0,0),(0,2,0,0)); 5 //1~9对乘积的影响 6 var v:array[0..60,0..38,0..30,0..22] of longint; 7 f,s:array[0..20,0..70010] of longint; 8 p,r,g,h:array[0..20] of longint; 9 pp:array[0..20] of int64; 10 n,a,b,c,d,i,j,t,m,k:longint; 11 x,y,w:int64; 12 13 procedure up(var a:longint; b:longint); 14 begin 15 a:=(a+b+mo) mod mo; 16 end; 17 18 procedure pre; 19 var i,j,k,p:int64; 20 a,b,c,d:longint; 21 begin 22 i:=1; 23 a:=0; 24 while i<=lim do 25 begin 26 j:=i; b:=0; 27 while j<=lim do 28 begin 29 k:=j; c:=0; 30 while k<=lim do 31 begin 32 p:=k; d:=0; 33 while p<=lim do 34 begin 35 inc(t); 36 v[a,b,c,d]:=t; 37 p:=p*7; 38 inc(d); 39 end; 40 k:=k*5; 41 inc(c); 42 end; 43 j:=j*3; 44 inc(b); 45 end; 46 i:=i*2; 47 inc(a); 48 end; 49 end; 50 51 procedure work; 52 var a,b,c,d,i,j:longint; 53 begin 54 f[0,1]:=1; 55 for i:=1 to 18 do 56 for a:=0 to 60 do for b:=0 to 38 do for c:=0 to 26 do for d:=0 to 22 do 57 if (v[a,b,c,d]>0) and (f[i-1,v[a,b,c,d]]>0) then 58 begin 59 for j:=1 to 9 do 60 begin 61 up(f[i,v[a+q[j,1],b+q[j,2],c+q[j,3],d+q[j,4]]],f[i-1,v[a,b,c,d]]); 62 //f[i,S]表示i位数乘积为S的数的个数 63 up(s[i,v[a+q[j,1],b+q[j,2],c+q[j,3],d+q[j,4]]],s[i-1,v[a,b,c,d]]+int64(f[i-1,v[a,b,c,d]])*int64(j)*int64(p[i]) mod mo); 64 //s[i,S]表示i位数乘积为S的数的和,转移就是算当前第i位对和的贡献,显然是j(第i位的数)*p[i](位权)*f[i-1,S/j] 65 end; 66 end 67 else if v[a,b,c,d]=0 then break; 68 h[0]:=1; 69 for i:=1 to 18 do 70 begin 71 h[i]:=h[i-1]*9 mod mo; //i位数不含0的个数 72 g[i]:=(g[i-1]*9+45*int64(p[i])*int64(h[i-1]) mod mo) mod mo; //i位数不含0的数的和,45=1+..+9 73 end; 74 end; 75 76 procedure get(x:int64); 77 begin 78 m:=0; 79 while x>0 do 80 begin 81 inc(m); 82 r[m]:=x mod 10; 83 x:=x div 10; 84 end; 85 end; 86 87 function can(x:int64):boolean; 88 begin 89 a:=0; b:=0; c:=0; d:=0; //拆分成2^a*3^b*5^c*7^d 90 while x mod 2=0 do 91 begin 92 inc(a); 93 x:=x div 2; 94 end; 95 while x mod 3=0 do 96 begin 97 inc(b); 98 x:=x div 3; 99 end; 100 while x mod 5=0 do 101 begin 102 inc(c); 103 x:=x div 5; 104 end; 105 while x mod 7=0 do 106 begin 107 inc(d); 108 x:=x div 7; 109 end; 110 if x=1 then exit(true) else exit(false); 111 end; 112 113 function sum(a:int64):longint; //防止爆int64的计算 114 var b:int64; 115 begin 116 b:=a-1; 117 if a mod 2=0 then a:=a div 2 118 else b:=b div 2; 119 a:=a mod mo; 120 b:=b mod mo; 121 exit(a*b mod mo); 122 end; 123 124 function count(x,k:int64):longint; 125 var i,j,pre,a1,b1,c1,d1,ans:longint; 126 now:int64; 127 begin 128 if not can(k) then exit(0); //k不存在 129 get(x); 130 ans:=0; 131 for i:=1 to m-1 do //统计1~m-1位数乘积为k数的和 132 up(ans,s[i,v[a,b,c,d]]); 133 now:=1; 134 pre:=0; //pre=∑(r[i]*p[i]) 135 for i:=m downto 1 do 136 begin 137 now:=now*int64(r[i]); 138 if r[i]=0 then break; //显然 139 for j:=1 to r[i]-1 do 140 begin 141 a1:=a-q[j,1]; b1:=b-q[j,2]; c1:=c-q[j,3]; d1:=d-q[j,4]; 142 if (a1<0) or (b1<0) or (c1<0) or (d1<0) then continue; 143 up(ans,s[i-1,v[a1,b1,c1,d1]]+int64(pre+j*p[i])*int64(f[i-1,v[a1,b1,c1,d1]]) mod mo); 144 //和与处理的转移类似 145 end; 146 a:=a-q[r[i],1]; b:=b-q[r[i],2]; c:=c-q[r[i],3]; d:=d-q[r[i],4]; 147 up(pre,r[i]*p[i] mod mo); 148 if (a<0) or (b<0) or (c<0) or (d<0) then break; 149 end; 150 if now=k then up(ans,x mod mo); //判断x 151 exit(ans); 152 end; 153 154 function calc(x:int64):longint; //乘积为0的转移要复杂,容易出错 155 var i,j,pre,ans,e:longint; 156 begin 157 get(x); 158 ans:=sum(p[m]) mod mo; //先求1~m-1乘积为0的数和,我们可以用补集的思想,总和-不含0的数和 159 for i:=1 to m-1 do 160 up(ans,-g[i]); 161 pre:=0; 162 for i:=m downto 1 do 163 begin 164 if r[i]=0 then 165 begin 166 up(ans,int64(pre)*int64(x mod pp[i] mod mo+1) mod mo+sum(x mod pp[i]+1)); 167 //当前为0,后面对答案的贡献可以直接算出来 168 break; 169 end; 170 if i<m then up(ans,int64(pre)*int64(p[i]) mod mo+sum(pp[i])); 171 //否则m位数且小于等于x的数当前位可以是0,计算贡献 172 if i=1 then e:=r[i] else e:=r[i]-1; 173 for j:=1 to e do 174 up(ans,sum(pp[i])-g[i-1]+int64(pre+j*p[i])*int64((p[i]-h[i-1]+mo) mod mo) mod mo); 175 //这里还是运用到补集的思想,和之前算乘积不为0类似 176 up(pre,r[i]*p[i] mod mo); 177 end; 178 exit(ans); 179 end; 180 181 begin 182 readln(n); 183 p[1]:=1; 184 pp[1]:=1; 185 for i:=2 to 18 do 186 begin 187 p[i]:=p[i-1]*10 mod mo; //位权 188 pp[i]:=pp[i-1]*10; 189 end; 190 pre; 191 work; 192 for i:=1 to n do 193 begin 194 readln(x,y,w); 195 if w=0 then writeln((calc(y)-calc(x-1)+mo) mod mo) 196 else writeln((count(y,w)-count(x-1,w)+mo) mod mo); 197 end; 198 end.