3131: [Sdoi2013]淘金
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 733 Solved: 363
[Submit][Status][Discuss]
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
1 2 5
Sample Output
18
HINT
N < = 10^12 ,K < = 100000
对于100%的测试数据:K < = N^2
久违的的数位DP题……
首先,我们可以发现横纵坐标没有任何关系,完全独立,所以我们真正要做的是求出每个数有多少个数以他为f值。按照套路我们还是应该先dfs一下各种可能的乘积,然后弱到一定地步的我就懵了,乘积得多少种啊,存都存不下,然后事实证明我错了,实践证明我只要打一下表就会发现其实只有八千多个乘积……
求出来所有乘积之后我们接着按照数位DP的套路搞。不过为了方便,我们把我们得到的所有乘积先去重和离散。设f[i][j][k]为当前我们算到第i位,第i位为j,当前乘积离散后为k的方案数。转移也是很好转移的,直接枚举那一位的数是几就好了。统计答案的时候也是按照套路,先统计位数比n低的,然后将位数从高到低依次统计每一个k答案即可。至于答案。我们用优先队列排序就可以了。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #include <cmath> 7 #include <queue> 8 #include <map> 9 #include <set> 10 #include <vector> 11 #define N 10005 12 using namespace std; 13 long long n; 14 int k,zz,cnt; 15 long long c[50],p=1000000007; 16 long long jg[1000005]; 17 void dfs(long long x,int st,int len) 18 { 19 if(len==14) 20 { 21 return; 22 } 23 zz++; 24 jg[zz]=x; 25 for(int i=st;i<=9;i++) 26 { 27 dfs(x*i*1ll,i,len+1); 28 } 29 } 30 long long f[15][10][N]; 31 long long ans[N]; 32 long long work(int x) 33 { 34 long long sum=0; 35 for(int i=1;i<cnt;i++) 36 { 37 for(int j=1;j<=9;j++) 38 { 39 sum+=f[i][j][x]; 40 } 41 } 42 long long la=1; 43 for(int i=cnt;i>0;i--) 44 { 45 if(la==0||jg[x]%la||la>jg[x])break; 46 int t=lower_bound(jg+1,jg+1+zz,jg[x]/la)-jg; 47 for(int j=1;j<c[i];j++) 48 { 49 sum+=f[i][j][t]; 50 } 51 la*=c[i]; 52 } 53 return sum; 54 } 55 map<int,bool> vi[N]; 56 struct no{ 57 int a,b; 58 long long data; 59 bool friend operator < (no a,no b) 60 { 61 return a.data<b.data; 62 } 63 }; 64 int main() 65 { 66 scanf("%lld%d",&n,&k); 67 zz++;jg[zz]=0; 68 long long tt=n; 69 while(tt) 70 { 71 cnt++; 72 c[cnt]=tt%10; 73 tt/=10; 74 } 75 c[1]++; 76 dfs(1,2,1); 77 78 sort(jg+1,jg+zz+1); 79 zz=unique(jg+1,jg+zz+1)-jg-1; 80 for(int i=1;i<=9;i++) 81 { 82 f[1][i][lower_bound(jg+1,jg+zz+1,i)-jg]=1; 83 } 84 for(int i=1;i<cnt;i++) 85 { 86 for(int j=1;j<=9;j++) 87 { 88 for(int k=1;k<=9;k++) 89 { 90 for(int l=1;l<=zz;l++) 91 { 92 if(!f[i][j][l])continue; 93 if(jg[l]*(long long)k>jg[zz])continue; 94 f[i+1][k][lower_bound(jg+1,jg+1+zz,jg[l]*k)-jg]+=f[i][j][l]; 95 } 96 } 97 } 98 } 99 for(int i=1;i<=zz;i++) 100 { 101 ans[i]=work(i); 102 } 103 sort(ans+1,ans+zz+1); 104 105 no aa; 106 aa.a=aa.b=zz; 107 aa.data=ans[zz]*ans[zz]; 108 priority_queue<no> q1; 109 110 q1.push(aa); 111 long long sum=0; 112 while(k&&!q1.empty()) 113 { 114 k--; 115 while(!q1.empty()&&vi[q1.top().a][q1.top().b]) q1.pop(); 116 aa=q1.top();q1.pop(); 117 sum+=aa.data%p; 118 sum%=p; 119 vi[aa.a][aa.b]=1; 120 aa.a--; 121 aa.data=ans[aa.a]*ans[aa.b]; 122 q1.push(aa); 123 aa.a++;aa.b--; 124 aa.data=ans[aa.a]*ans[aa.b]; 125 q1.push(aa); 126 } 127 printf("%lld ",sum); 128 return 0; 129 }