这是一道很好也很烦的综合题……
首先我们肯定要先把f(i)处理出来这是毫无疑问的
我们要求出数位乘积为now的个数,首先是空间上的问题
直接肯定会爆空间,不难发现
乘积的质因数只有2,3,5,7,并且指数也不是特别大
暴力可得到不同的乘积最多只有15000不到
然后我们就可以对其离散化然后数位dp
dp完之后,对于点(p,q),这上面的金子个数是sum(p)*sum(q)个 (sum表示数位乘积为p的数的个数)
然后我们要求金子前k多的点,当状态太多无法算出所有状态求最优值时,我们常常用堆来维护
首先我们对sum排序(假定降序),对于每个乘积x,设乘积x离散化后对应的编号为w(x)
在x行上金子最多的点一定是sum[w(x)]*sum[1],次大的点一定是sum[w(x)]*sum[2]……
然后我们对当前这n行上最多的点维护一个大根堆
当我们每次将堆顶的点取出时,堆顶所在行的下一大的点可能是之后选取结果产生影响,
因此我们要将这行下一大的点加入堆,一共只要弹k次,所以复杂度为O(klogt)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 const maxn=200010; 2 mo=1000000007; 3 4 type arr=array[0..maxn] of int64; 5 6 var a,sum,h,loc,num:arr; 7 b:array[0..30] of int64; 8 f:array[0..20,0..maxn] of int64; 9 n,ans,x:int64; 10 c,m,t,i:longint; 11 12 procedure swap(var a,b:int64); 13 var c:int64; 14 begin 15 c:=a; 16 a:=b; 17 b:=c; 18 end; 19 20 procedure sort(l,r:longint;var a:arr); 21 var i,j:longint;x,y:int64; 22 begin 23 i:=l; 24 j:=r; 25 x:=a[(l+r) shr 1]; 26 repeat 27 while a[i]<x do inc(i); 28 while x<a[j] do dec(j); 29 if i<=j then 30 begin 31 swap(a[i],a[j]); 32 inc(i); 33 dec(j); 34 end; 35 until i>j; 36 if i<r then sort(i,r,a); 37 if j>l then sort(l,j,a); 38 end; 39 40 procedure prepare; 41 var i,j,k,l:int64; 42 begin 43 i:=1; 44 while i<=n do 45 begin 46 j:=i; 47 while j<=n do 48 begin 49 k:=j; 50 while k<=n do 51 begin 52 l:=k; 53 while l<=n do 54 begin 55 inc(m); 56 a[m]:=l; 57 l:=l*7; 58 end; 59 k:=k*5; 60 end; 61 j:=j*3; 62 end; 63 i:=i*2; 64 end; 65 sort(1,m,a); 66 end; 67 68 procedure work; 69 begin 70 t:=0; 71 x:=n; 72 while x<>0 do 73 begin 74 inc(t); 75 b[t]:=x mod 10; 76 x:=x div 10; 77 end; 78 end; 79 80 function find(l,r:longint;x:int64):longint; 81 var mid:longint; 82 begin 83 while l<r do 84 begin 85 mid:=(l+r)shr 1; 86 if a[mid]=x then exit(mid); 87 if a[mid]<x then l:=mid+1 else r:=mid-1; 88 end; 89 if a[l]=x then exit(l); 90 exit(0); 91 end; 92 93 procedure count; 94 var i,j,k:longint; 95 now,y:int64; 96 begin 97 work; 98 f[0][1]:=1; //f[i,j]表示到第i位,乘积为j(j是离散化后的排名)的方案数 99 for i:=1 to t-1 do 100 for j:=1 to m do 101 for k:=1 to 9 do 102 if (a[j] mod k=0) then 103 f[i,j]:=f[i,j]+f[i-1,find(1,j,a[j] div k)]; 104 105 now:=1; 106 for i:=t downto 1 do 107 begin 108 for j:=1 to b[i]-1 do //肯定不可能是0 109 for k:=1 to m do 110 if (a[k]>=now*j) and ((a[k] div now) mod j=0) and (a[k] mod now=0) then 111 sum[k]:=sum[k]+f[i-1,find(1,k,a[k] div (now*j))]; 112 now:=now*b[i]; 113 if now=0 then break; 114 end; 115 if now<>0 then inc(sum[find(1,m,now)]); 116 for i:=1 to m do 117 for j:=1 to t-1 do 118 sum[i]:=sum[i]+f[j,i]; 119 end; 120 121 procedure sift(i,n:longint); 122 var j:longint; 123 x:int64; 124 begin 125 x:=h[i]; 126 j:=i shl 1; 127 while j<=n do 128 begin 129 if (j<n) and (h[j]<h[j+1]) then inc(j); 130 if x>=h[j] then exit 131 else begin 132 swap(h[i],h[j]); 133 swap(loc[i],loc[j]); 134 swap(num[i],num[j]); 135 i:=j; 136 j:=j shl 1; 137 end; 138 end; 139 end; 140 141 begin 142 readln(n,c); 143 prepare; 144 work; 145 count; 146 sort(1,m,sum); //为了统一形式,这里是升序 147 for i:=1 to m do 148 begin 149 h[i]:=sum[i]*sum[m]; //当前第i行上最大的点 150 loc[i]:=m; 151 num[i]:=i; //所代表的行 152 end; 153 for i:=m downto 1 do 154 sift(i,m); 155 ans:=0; 156 for i:=1 to c do 157 begin 158 ans:=(ans+(h[1] mod mo)) mod mo; 159 dec(loc[1]); //加入这行下一大的点 160 h[1]:=sum[num[1]]*sum[loc[1]]; 161 sift(1,m); 162 end; 163 writeln(ans); 164 end.