Description
小Z在玩一个叫做《淘金者》的游戏。游戏的世界是一个二维坐标。X轴、Y轴坐标范围均为1..N。初始的时候,所有的整数坐标点上均有一块金子,共N*N块。
一阵风吹过,金子的位置发生了一些变化。细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),fIj))坐标处。其中f(x)表示x各位数字的乘积,例如f(99)=81,f(12)=2,f(10)=0。如果金子变化后的坐标不在1..N的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。
小Z很懒,打算只进行K次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为0。
现在小Z希望知道,对于变化之后的游戏局面,在采集次数为K的前提下,最多可以采集到多少块金子?
答案可能很大,小Z希望得到对1000000007(10^9+7)取模之后的答案。
Input
共一行,包含两介正整数N,K。
Output
一个整数,表示最多可以采集到的金子数量。
Sample Input
Sample Output
HINT
N < = 10^12 ,K < = 100000
对于100%的测试数据:K < = N^2
题解:
开始想偏了……导致整个人都是懵逼的。
首先打个表发现,各位数字乘积不同结果大概是8000左右,然后开始乱搞。
设$f_{i,j,k}$表示总共i位,开头数字为j,乘积排名为k的总数,转移就比较显然。
然后枚举每个k,接下来就是基本的数位DP了,这样我们求出了某一列上每个位置的金块数量$ans_{k}$,然后画个图发现,在$(i,j)$位置上的数量为我们刚才DP出的$ans_{i}*ans_{j}$,给$ans$排个序,然后贪心即可。
代码:
1 #define Troy 10/11/2017 2 3 #include <bits/stdc++.h> 4 5 using namespace std; 6 7 typedef long long ll; 8 9 const ll mod=1e9+7; 10 11 inline ll read(){ 12 ll s=0,k=1;char ch=getchar(); 13 while(ch<'0'|ch>'9') ch=='-'?k=-1:0,ch=getchar(); 14 while(ch>47&ch<='9') s=s*10+(ch^48),ch=getchar(); 15 return s*k; 16 } 17 18 const int N=1e6; 19 20 ll has[N];int num,p[20],tot; 21 22 inline void dfs(ll x,int from,int step){ 23 if(step>12) return ; 24 has[++num]=x; 25 for(;from<10;from++){ 26 dfs(x*from,from,step+1); 27 } 28 } 29 30 ll f[15][15][10000],n,k,ans[10000];//f[i][j][k]:i位开头为j乘积为k的答案 31 32 inline void init(){ 33 has[++num]=0; 34 dfs(1,2,0); 35 sort(has+1,has+num+1); 36 num=unique(has+1,has+num+1)-has-1;// printf("num=%d ",num); 37 for(int i=1;i<=9;i++) 38 f[1][i][lower_bound(has+1,has+num+1,i)-has]=1; 39 for(int i=2;i<=13;i++){ 40 for(int j=1;j<10;j++){ 41 for(int k=1;k<10;k++) 42 for(int l=1;l<=num;l++){ 43 if(f[i-1][k][l]==0) continue; 44 int x; 45 if(has[l]*j<=has[num]){ 46 x=lower_bound(has+1,has+num+1,has[l]*j)-has; 47 f[i][j][x]+=f[i-1][k][l]; 48 } 49 } 50 } 51 } 52 do{ 53 p[++tot]=n%10; 54 n/=10; 55 }while(n); 56 } 57 58 inline ll calc(int pos){ 59 ll ret=0; 60 for(int i=1;i<tot;i++){ 61 for(int j=1;j<=9;j++){ 62 ret+=f[i][j][pos]; 63 } 64 } 65 ll last=1; 66 for(int i=tot;i;i--){ 67 if(last>has[pos]||last==0||has[pos]%last!=0) break; 68 for(int j=1;j<p[i];j++){ 69 int x=lower_bound(has+1,has+1+num,has[pos]/last)-has; 70 ret+=f[i][j][x]; 71 } 72 last*=p[i]; 73 } 74 return ret%mod; 75 } 76 77 bool vis[9000][9000]; 78 79 struct node { 80 int pos[2];ll val; 81 friend bool operator <(node a,node b){ 82 return a.val!=b.val?a.val<b.val:a.pos[0]!=b.pos[0]?a.pos[0]>b.pos[0]:a.pos[1]>b.pos[1]; 83 } 84 }; 85 86 priority_queue<node> q; 87 88 int main(){ 89 n=read(),k=read(); 90 n++; 91 init(); 92 for(int i=2;i<=num;i++) 93 ans[i]=calc(i); 94 sort(ans+2,ans+num+1); 95 q.push((node){num,num,ans[num]*ans[num]}); 96 ll ret=0; 97 while(k&&(!q.empty())){ 98 k--; 99 node now; 100 do{ 101 now=q.top();q.pop(); 102 }while(vis[now.pos[0]][now.pos[1]]&&(!q.empty())); 103 if(!vis[now.pos[0]][now.pos[1]]){ 104 ret+=now.val; 105 ret%=mod; 106 if(now.pos[0]>2) 107 q.push((node){now.pos[0]-1,now.pos[1],ans[now.pos[0]-1]*ans[now.pos[1]]}); 108 if(now.pos[1]>2) 109 q.push((node){now.pos[0],now.pos[1]-1,ans[now.pos[0]]*ans[now.pos[1]-1]}); 110 } 111 vis[now.pos[0]][now.pos[1]]=true; 112 } 113 printf("%lld ",ret); 114 }