bzoj千题计划268:bzoj3131: [Sdoi2013]淘金
http://www.lydsy.com/JudgeOnline/problem.php?id=3131
如果已知 s[i]=j 表示有j个<=n数的数码乘积=i
那么就会有 s[a1]*s[a2] 个数 在一阵风之后到(a1,a2)位置
把所有的j用一个数组b存起来,从大到小排序
开始把(1,1)存入堆,表示当前最多的是b[1]*b[1]
每次取出堆顶(i,j),累加 b[i]*b[j],存入(i+1,j),(i,j+1),map 判重
就可以解决前k大之和的问题
i的上限是n,存不下,怎么办
我们发现 n以内 有大量的i 是无用的
只有质因数分解之后 质因子为2、3、5、7 的i 才是合法状态
所以 dp[len][0/1][c2][c3][c5][c7] 表示前len位,是否卡上界,当前i=2^c2 * 3^c3 * 5^c5 * 7^c7 的 j是多少
数位dp
注意不能放0,但是可以有前导0,所以除了最高位,都要加上前导0的贡献,即dp[][0][0][0][0][0]++ (前导0不卡上界)
#include<map> #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const int mod=1e9+7; LL dp[2][2][41][27][19][16]; int f[10][4]; int a[14]; int cnt; LL b[10001]; priority_queue< pair<LL,pair<int,int> > >q; map<pair<int,int>,bool>vis; void pre() { f[2][0]=1; f[3][1]=1; f[4][0]=2; f[5][2]=1; f[6][0]=1; f[6][1]=1; f[7][3]=1; f[8][0]=3; f[9][1]=2; } void reverse(int len) { int k=len/2; for(int i=1;i<=k;++i) swap(a[i],a[len-i+1]); } void ADD(int &x,int y) { x+=y; x-=x>=mod ? mod : 0; } LL Pow(LL a,int b) { LL res=1; for(;b;a*=a,b>>=1) if(b&1) res*=a; return res; } void numberDP(LL n) { int len=0; LL m=n; while(m) a[++len]=m%10,m/=10; reverse(len); int up,J; int now=0,nxt=1; for(int i=0;i<len;++i,swap(now,nxt)) { if(!i) dp[now][1][0][0][0][0]++; else dp[now][0][0][0][0][0]++; memset(dp[nxt],0,sizeof(dp[nxt])); for(int j=0;j<=1;++j) for(int c2=0;c2<=40;++c2) for(int c3=0;c3<=26;++c3) for(int c5=0;c5<=18;++c5) for(int c7=0;c7<=15;++c7) if(dp[now][j][c2][c3][c5][c7]) { if(j) up=a[i+1]; else up=9; for(int k=1;k<=up;++k) { J=(j && k==up); dp[nxt][J][c2+f[k][0]][c3+f[k][1]][c5+f[k][2]][c7+f[k][3]]+=dp[now][j][c2][c3][c5][c7]; } } } LL sum,tmp; for(int c2=0;c2<=40;++c2) { tmp=Pow(2,c2); if(tmp>n) break; for(int c3=0;c3<=26;++c3) { tmp=Pow(2,c2)*Pow(3,c3); if(tmp>n) break; for(int c5=0;c5<=18;++c5) { tmp=Pow(2,c2)*Pow(3,c3)*Pow(5,c5); if(tmp>n) break; for(int c7=0;c7<=15;++c7) { tmp=Pow(2,c2)*Pow(3,c3)*Pow(5,c5)*Pow(7,c7); if(tmp>n) break; sum=dp[now][0][c2][c3][c5][c7]+dp[now][1][c2][c3][c5][c7]; if(sum) b[++cnt]=sum;/*,printf("%d %d %d %d %I64d\n",c2,c3,c5,c7,sum)*/; } } } } } void get_kth(int k) { sort(b+1,b+cnt+1,greater<int>()); int i=1,j=1; int ans=0; pair<LL,pair<int,int> >pr; int x,y; q.push(make_pair(b[1]*b[1],make_pair(1,1))); while(k-- && !q.empty()) { pr=q.top(); q.pop(); if(!pr.first) break; ADD(ans,pr.first%mod); x=pr.second.first; y=pr.second.second; if(!vis[make_pair(x+1,y)]) { q.push(make_pair(b[x+1]*b[y],make_pair(x+1,y))); vis[make_pair(x+1,y)]=true; } if(!vis[make_pair(x,y+1)]) { q.push(make_pair(b[x]*b[y+1],make_pair(x,y+1))); vis[make_pair(x,y+1)]=true; } } printf("%d",ans); } int main() { //freopen("gold.in","r",stdin); //freopen("gold.out","w",stdout); LL n; int k; scanf("%lld%d",&n,&k); pre(); numberDP(n); get_kth(k); return 0; }