题意
小Z在玩一个叫做《淘金者》的游戏。游戏的世界是一个二维坐标。X轴、Y轴坐标范围均为1..N。初始的时候,所有的整数坐标点上均有一块金子,共N*N块。
一阵风吹过,金子的位置发生了一些变化。细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),f(j))坐标处。其中f(x)表示x各位数字的乘积,例如f(99)=81,f(12)=2,f(10)=0。如果金子变化后的坐标不在1..N的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。
小Z很懒,打算只进行K次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为0。
现在小Z希望知道,对于变化之后的游戏局面,在采集次数为K的前提下,最多可以采集到多少块金子?
答案可能很大,小Z希望得到对1000000007(10^9+7)取模之后的答案。
N < = 10^12 ,K < = 100000
分析
注意到一个点的坐标(x,y)的金子数量就等于s(x)*s(y),其中s(x)表示有多少个不大于的整数其各个位乘积为x。那么只要求出所有的s(x)即可。
由于s(x)中的x仍然很大,所以不能直接dp。注意到假如s(x)不为0,则x必然只含2,3,5,7这4个质因数,那么我们就可以考虑对素因数的指数进行数位dp。
设f[i,x1,x2,x3,x4,0/1]表示从高到底做到n的第i位,2的指数为x1,3的指数为x2,5的指数为x3,7的指数为x4且是否卡着n的上界的数的数量。直接瞎转移即可。
求出所有的s(x),可以用堆来维护两个数乘积的前k大。因为我比较懒所以直接用map来判断数对(x,y)是否出现过。
由于一开始没有想清楚就开始打,导致后面发现很多地方都白打了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
const int MOD=1000000007;
int m,tr[10][5],f[2][45][45][45][45][2],a[15],val[45*45*45*45];
LL n;
priority_queue<pair<LL,pair<int,int> > > que;
map<pair<int,int>,bool> vis;
void updata(int &x,int y)
{
x+=y;
}
void prework()
{
tr[2][1]=1;
tr[3][2]=1;
tr[4][1]=2;
tr[5][3]=1;
tr[6][1]=tr[6][2]=1;
tr[7][4]=1;
tr[8][1]=3;
tr[9][2]=2;
}
int main()
{
prework();
scanf("%lld%d",&n,&m);
int t1=0,t2=0,t3=0,t4=0;LL t;
t=1;while (t*2<=n) t1++,t=t*2;
t=1;while (t*3<=n) t2++,t=t*3;
t=1;while (t*5<=n) t3++,t=t*5;
t=1;while (t*7<=n) t4++,t=t*7;
int now=0,a1=0;LL tmp=n;
while (tmp) a[++a1]=tmp%10,tmp/=10;
f[now][0][0][0][0][1]=1;
for (int i=a1;i>=1;i--)
{
memset(f[now^1],0,sizeof(f[now^1]));
for (int x1=t1;x1>=0;x1--)
for (int x2=t2;x2>=0;x2--)
for (int x3=t3;x3>=0;x3--)
for (int x4=t4;x4>=0;x4--)
{
if (f[now][x1][x2][x3][x4][0])
{
for (int j=1;j<=9;j++) updata(f[now^1][x1+tr[j][1]][x2+tr[j][2]][x3+tr[j][3]][x4+tr[j][4]][0],f[now][x1][x2][x3][x4][0]);
}
if (f[now][x1][x2][x3][x4][1])
{
if (a[i]) updata(f[now^1][x1+tr[a[i]][1]][x2+tr[a[i]][2]][x3+tr[a[i]][3]][x4+tr[a[i]][4]][1],f[now][x1][x2][x3][x4][1]);
for (int j=1;j<a[i];j++) updata(f[now^1][x1+tr[j][1]][x2+tr[j][2]][x3+tr[j][3]][x4+tr[j][4]][0],f[now][x1][x2][x3][x4][1]);
}
}
now^=1;
f[now][0][0][0][0][0]++;
}
int tot=0;
f[now][0][0][0][0][0]--;
for (int i=0;i<=t1;i++)
for (int j=0;j<=t2;j++)
for (int k=0;k<=t3;k++)
for (int l=0;l<=t4;l++)
{
int s=f[now][i][j][k][l][0]+f[now][i][j][k][l][1];
if (s) val[++tot]=s;
}
sort(val+1,val+tot+1);
que.push(make_pair((LL)val[tot]*val[tot],make_pair(tot,tot)));
LL ans=0;
while (m--)
{
if (que.empty()) break;
pair<LL,pair<int,int> > u=que.top();que.pop();
(ans+=u.first)%=MOD;
int x=u.second.first,y=u.second.second;
if (y&&!vis[make_pair(x,y-1)]) que.push(make_pair((LL)val[x]*val[y-1],make_pair(x,y-1))),vis[make_pair(x,y-1)]=1;
if (x&&!vis[make_pair(x-1,y)]) que.push(make_pair((LL)val[x-1]*val[y],make_pair(x-1,y))),vis[make_pair(x-1,y)]=1;
}
printf("%lld",ans);
return 0;
}