bzoj 3131: [Sdoi2013]淘金 数位dp+堆
2017-12-12 21:43
330 查看
题意
小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; }
相关文章推荐
- bzoj 3131 [Sdoi2013]淘金(数位dp)
- [Bzoj3131][Sdoi2013]淘金(数位dp)(优先队列)
- [BZOJ3131][Sdoi2013]淘金(数位DP+贪心+堆)
- 【bzoj 3131】[Sdoi2013]淘金
- bzoj 3131: [Sdoi2013]淘金
- BZOJ 3131 [Sdoi2013] 淘金
- bzoj 3131 [Sdoi2013]淘金(数位DP+优先队列)
- bzoj 3107: [cqoi2013]二进制a+b 数位dp
- [BZOJ3530][SDOI2014]数数 AC自动机+数位DP
- 【BZOJ3124】[Sdoi2013]直径 树形DP(不用结论)
- [您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)
- BZOJ 4513: [Sdoi2016]储能表 数位dp
- 【BZOJ3530】[Sdoi2014]数数 AC自动机+数位DP
- BZOJ 4513 [SDOI2016]储能表 【数位DP】
- [BZOJ3124][Sdoi2013]直径(树形dp)
- bzoj 3530: [Sdoi2014]数数 (AC自动机+数位DP)
- bzoj3134 [Baltic2013]numbers-数位dp
- [BZOJ3530]-[Sdoi2014]数数-AC自动机+数位DP
- [BZOJ3124][Sdoi2013]直径(树形dp)
- BZOJ 3530: [Sdoi2014]数数 [AC自动机 数位DP]