[BZOJ 3131][Sdoi2013]淘金

题目连接:http://www.lydsy.com/JudgeOnline/problem.php?id=3131

首先可以想到先算出一维的,再拓展到二维。

我们设g[i]表示各位数字乘积为i的数字个数(<=n),然后可以用堆/优先队列算出二维的前k大。

现在关键是如何求g[i]。

我一开始也不会~(≧▽≦)/~啦啦啦,但实际上所有乘积总个数只有2e5多(我也不知道为什么有题解说是1e4。。。可能是我错了吧),将乘积离散化,然后就可以进行数位DP了。具体如下:设f[i][j][k]表示当前算到第i位,乘积下标为j,到目前为止是否大于n。转移时要二分乘积的位置,所以复杂度是位数*乘积个数*log乘积个数*2

注:stl是个好东西啊,unique真方便~

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<string>
#include<ctime>
#include<queue>
#include<map>
#include<set>
#include<vector>
#define LL long long
using namespace std;
const int N=400010,mod=1e9+7;
struct node
{
    int x,y; LL z;
    bool operator < (const node &x) const{return z<x.z;}
};
priority_queue<node>q;
LL n,f[15][N][2],g[N],a[N];
int k,cnt,num[15],len;
LL read() {LL d=0,f=1; char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') d=(d<<3)+(d<<1)+c-48,c=getchar(); return d*f;}
void judge(){freopen(".in","r",stdin); freopen(".out","w",stdout);}
void dfs(int k,int l,LL now)
{
    if (l>len) {a[++cnt]=now; return;}
    for (int i=k;i<10;i++) dfs(i,l+1,now*i);
}
bool cmp(LL a,LL b){return a>b;}
node make(int x,int y)
{
    node res;
    res.x=x; res.y=y; res.z=g[x]*g[y];
    return res;
}
int main()
{
    //judge();
    n=read(); k=read();
    for (;n;n/=10) num[++len]=n%10;
    a[cnt=1]=0;
    dfs(1,1,1);
    sort(a+1,a+1+cnt);
    cnt=unique(a+1,a+1+cnt)-a-1;
    f[0][2][0]=1;
    for (int i=0;i<=len;i++)
        for (int j=1;j<=cnt;j++)
            for (int k=0;k<=1;k++) if (f[i][j][k])
                for (int l=i?1:0;l<10;l++)
                {
                    int pjy=lower_bound(a+1,a+1+cnt,a[j]*l)-a;
                    f[i+1][pjy][k+l>num[i+1]]+=f[i][j][k];
                }
    for (int i=1;i<=cnt;i++)
    {
        for (int j=1;j<len;j++) g[i]+=f[j][i][0]+f[j][i][1];
        g[i]+=f[len][i][0];
    }
    sort(g+1,g+1+cnt,cmp);
    LL ans=0;
    q.push(make(2,2));
    while (!q.empty())
    {
        node ljj=q.top(); q.pop();
        ans=(ans+ljj.z)%mod;
        if (--k==0) break;
        if (ljj.x!=ljj.y)
        {
            ans=(ans+ljj.z)%mod;
            if (--k==0) break;
            q.push(make(ljj.x+1,ljj.y));
        }
        if (ljj.x==2) q.push(make(ljj.x,ljj.y+1));
    }
    printf("%d",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/lujiaju6555/p/6718410.html