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)取模之后的答案。
N < = 10^12 ,K < = 100000且K < = N^2
Solution
一个直观的想法就是数位dp,然而如果把n放进状态里面会爆。
感受一波可以发现有金子的数字不会太多,考虑质因数分解这些数字,我们发现一定能表示成2x1×3x2×5x3×7x42x1×3x2×5x3×7x4这样的
然后就能数位dp了,设f[i,x1,x2,x3,x4,0/1]表示如上,套路转移
于是变成了给定a[],令b[i,j]=a[i]*a[j]这样求前k大和的问题,可以二分答案也可以用堆
需要注意的是可能存在取0的情况
当然第一步那里离散做也是可以的
Code
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
typedef std:: pair <int,int> pair;
typedef long long LL;
const int MOD=1000000007;
std:: vector <LL> vec;
struct data {
int fi,se;
bool operator <(data b) const {
return vec[fi]*vec[se]<vec[b.fi]*vec[b.se];
}
} ;
std:: map <pair,bool> vis;
std:: priority_queue <data> heap;
int f[14][42][28][20][17][2];
int a[14],rec[10][5];
bool operator >(data a,data b) {
return vec[a.fi]*vec[a.se]<vec[b.fi]*vec[b.se];
}
void mod(int &x) {
(x>=MOD)?(x-=MOD):(0);
(x<0)?(x+=MOD):(0);
}
int main(void) {
rec[2][1]=1; rec[3][2]=1; rec[4][1]=2; rec[5][3]=1; rec[6][1]=rec[6][2]=1; rec[7][4]=1; rec[8][1]=3; rec[9][2]=2;
LL n,k,len=0; scanf("%lld%lld",&n,&k);
int lim1=0; for (LL tmp=1;tmp*2LL<=n;tmp*=2LL,lim1++);
int lim2=0; for (LL tmp=1;tmp*3LL<=n;tmp*=3LL,lim2++);
int lim3=0; for (LL tmp=1;tmp*5LL<=n;tmp*=5LL,lim3++);
int lim4=0; for (LL tmp=1;tmp*7LL<=n;tmp*=7LL,lim4++);
for (;n;n/=10) a[++len]=n%10;
f[len][0][0][0][0][1]=1;
drp(i,len,1) {
rep(x1,0,lim1) rep(x2,0,lim2) rep(x3,0,lim3) rep(x4,0,lim4) {
int now0=f[i][x1][x2][x3][x4][0];
int now1=f[i][x1][x2][x3][x4][1];
if (now0) rep(num,1,9) {
mod(f[i-1][x1+rec[num][1]][x2+rec[num][2]][x3+rec[num][3]][x4+rec[num][4]][0]+=now0);
}
if (now1) if (a[i]) {
rep(num,1,a[i]-1) {
mod(f[i-1][x1+rec[num][1]][x2+rec[num][2]][x3+rec[num][3]][x4+rec[num][4]][0]+=now1);
}
mod(f[i-1][x1+rec[a[i]][1]][x2+rec[a[i]][2]][x3+rec[a[i]][3]][x4+rec[a[i]][4]][1]+=now1);
}
}
f[i-1][0][0][0][0][0]++;
}
f[0][0][0][0][0][0]--;
rep(x1,0,lim1) rep(x2,0,lim2) rep(x3,0,lim3) rep(x4,0,lim4) {
LL sum=f[0][x1][x2][x3][x4][0]+f[0][x1][x2][x3][x4][1];
if (sum) vec.push_back(sum);
}
std:: sort(vec.begin(), vec.end());
for (int i=0;i<vec.size();++i) {
heap.push((data) {i,(int)vec.size()-1});
}
LL ans=0;
for (;k;) {
if (heap.empty()) break;
data top=heap.top(); heap.pop();
if (vis[pair(top.fi,top.se)]) continue; vis[pair(top.fi,top.se)]=true;
ans=(ans+vec[top.fi]*vec[top.se]%MOD)%MOD;
if (top.se>0) heap.push((data) {top.fi,top.se-1});
k--;
}
printf("%lld\n", ans);
return 0;
}

本文介绍了一种解决淘金者游戏中金子位置变化问题的方法,通过数位DP算法结合质因数分解,优化了游戏中的金子采集策略,旨在最大化K次采集所能获得的金子数量。
545

被折叠的 条评论
为什么被折叠?



