题目描述
小 Z 在玩一个叫做《淘金者》的游戏。游戏的世界是一个二维坐标。XX 轴、YY 轴坐标范围均为1…N。初始的时候,所有的整数坐标点上均有一块金子,共 N2 块。
一阵风吹过,金子的位置发生了一些变化。细心的小 Z 发现,初始在 (i,j)坐标处的金子会变到 (f(i),f(j))(f(i),f(j)) 坐标处。其中 f(x)f(x) 表示 xx 各位数字的乘积,例如 f(99)=81, f(12)=2, f(10)=0。
如果金子变化后的坐标不在 1…N的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。
小 Z 很懒,打算只进行 K 次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为 0。
现在小 Z 希望知道,对于变化之后的游戏局面,在采集次数为 K 的前提下,最多可以采集到多少块金子? 答案可能很大,小 Z 希望得到对 109+7 取模之后的答案。
输入格式
共一行,包含两个正整数 N,K。
输出格式
一个整数,表示最多可以采集到的金子数量。
输入输出样例
输入 #1
12 5
输出 #1
18
c(x)c(x) 为满足 f(i)=xf(i)=x 即各位数字之积 =x=x 的 ii 的个数。
那么变化之后格子 (x,y)(x,y) 上有的金块个数就是 c(x)⋅c(y)c(x)⋅c(y)。
容易发现 c(x)c(x) 中的 xx 分解后若除了 2,3,5,72,3,5,7 还有其他质因子,c(x)=0c(x)=0。
问题就在于 c(x)c(x) 的求法,我们可以用数位 DP(个人喜欢记搜),状态需要记录搜到第 xx 位,组成的数 =2a⋅3b⋅5c⋅7d=2a⋅3b⋅5c⋅7d 中的指数 a,b,c,da,b,c,d。
然后用一个常量数组记录一下 1−91−9 分解之后的四个指数,搜索的时候注意除了前导 00 的时候都不能选 00,以及所有位都是 00 时最后一位会算作前导 00 需要特判。
于是我们 DFS 一遍 nn 以内的质因子只有 2,3,5,72,3,5,7 的数(或者直接枚举四个指数就行),求出其对应的 cc 函数值,然后把所有的 cc 函数值从大到小排个序。
然后我们设 cc 函数值有 cntcnt 个,往大根堆里面丢 cntcnt 个形如 (x,1)(x,1) 的二元组,大根堆的优先级是二元组两个数的 cc 值之积。
从大根堆取出一个二元组,计算对答案的贡献,再把第二个数加一丢回去。这样执行 kk 次就行了。
注意实现细节。
以及,不要问为什么我用 pbds 的优先队列。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
const int LEN = 12;
const int divi[10][4] = {
{0,0,0,0},
{0,0,0,0},
{1,0,0,0},
{0,1,0,0},
{2,0,0,0},
{0,0,1,0},
{1,1,0,0},
{0,0,0,1},
{3,0,0,0},
{0,2,0,0}
};
const long long N = 1e12;
const int CNT = 2e4;
const long long mod = 1e9 + 7;
long long n;
int k;
int tot,d[LEN + 5];
long long f[LEN + 5][LEN * 3 + 5][LEN * 2 + 5][LEN + 5][LEN + 5][2];
long long h[CNT + 5],c[CNT + 5];
int cnt;
void dfs(int x,long long prod)
{
static const long long d[4] = {2,3,5,7};
if(prod > n)
return ;
h[++cnt] = prod;
for(register int i = x;i < 4;++i)
dfs(i,prod * d[i]);
}
long long dfs(int x,int _2,int _3,int _5,int _7,int lead,int top)
{
if(_2 < 0 || _3 < 0 || _5 < 0 || _7 < 0)
return 0;
if(!x)
return !_2 && !_3 && !_5 && !_7;
if(!top && ~f[x][_2][_3][_5][_7][lead])
return f[x][_2][_3][_5][_7][lead];
long long ret = 0;
int bound = top ? d[x] : 9;
for(register int i = x > 1 ? 0 : 1;i <= bound;++i)
{
if(!lead && !i)
continue;
ret += dfs(x - 1,_2 - divi[i][0],_3 - divi[i][1],_5 - divi[i][2],_7 - divi[i][3],lead && !i,top && i == bound);
}
if(!top)
f[x][_2][_3][_5][_7][lead] = ret;
return ret;
}
long long solve(long long x)
{
int _2 = 0;
while(!(x % 2))
x /= 2,++_2;
int _3 = 0;
while(!(x % 3))
x /= 3,++_3;
int _5 = 0;
while(!(x % 5))
x /= 5,++_5;
int _7 = 0;
while(!(x % 7))
x /= 7,++_7;
return dfs(tot,_2,_3,_5,_7,1,1);
}
struct note
{
int x,y;
inline bool operator<(const note &a) const
{
return c[x] * c[y] < c[a.x] * c[a.y];
}
};
__gnu_pbds::priority_queue<note> pq;
long long ans;
int main()
{
memset(f,-1,sizeof f);
scanf("%lld%d",&n,&k);
dfs(0,1);
long long temp = n;
while(temp)
{
d[++tot] = temp % 10;
temp /= 10;
}
for(register int i = 1;i <= cnt;++i)
c[i] = solve(h[i]);
sort(c + 1,c + cnt + 1,greater<long long>());
k = min(k,cnt * cnt);
for(register int i = 1;i <= cnt;++i)
pq.push((note){i,1});
for(register int i = 1;i <= k;++i)
{
note cur = pq.top();
pq.pop();
ans = (ans + c[cur.x] * c[cur.y]) % mod;
if(cur.y == cnt)
continue;
pq.push((note){cur.x,cur.y + 1});
}
printf("%lld\n",ans);
}
在百忙之中写一篇题解也比较辛苦,别忘了点个赞!