[bzoj3131]淘金

[bzoj3131]淘金


观察到单个坐标可能的值很少,离散化出来数位dp,然后贪心选坐标即可

  • 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;int k;
ll pw[20];
vector<ll>nu;
map<ll,int>mp;
int TP;
inline void dfs(int nownum,int Q,ll nsum){
    if(Q==12){
        nu.push_back(nsum);return;
    }
    if(nownum<9)dfs(nownum+1,Q,nsum);
    dfs(nownum,Q+1,nsum*nownum);
}
int tot;
ll cont[10000],numb[10000];
ll f[15][15][10000][2];
inline void dp(int T){//T位数字
    f[T][T+1][1][pw[T]>n]=1;
    for(int w=T;w;w--){
        int K=(n/pw[w-1])%10;
        for(int i=1;i<=9;i++)
            for(int j=1;j<=tot;j++){
                if(f[T][w+1][j][0]) f[T][w][mp[numb[j]*i]][0]+=f[T][w+1][j][0];
                if(f[T][w+1][j][1]){
                    if(i<K)f[T][w][mp[numb[j]*i]][0]+=f[T][w+1][j][1];
                    else if(i==K)f[T][w][mp[numb[j]*i]][1]+=f[T][w+1][j][1];
                }
            }
    }
    for(int i=1;i<=tot;i++)
        cont[i]+=f[T][1][i][0]+f[T][1][i][1];
}
ll s[10000];int NN;
struct pls{
    int l,r;
    bool operator < (const pls b)const{return s[l]*s[r] < s[b.l]*s[b.r];}
};
ll ans=0,mod=1e9+7;
priority_queue<pls>Q;
inline void ccnntt(){
    sort(cont+1,cont+tot+1);
    for(int i=tot;i;i--)if(cont[i])s[++NN]=cont[i];
    for(int i=1;i<=NN;i++)Q.push((pls){1,i});
    while(k--){
        if(Q.empty())break;
        pls u=Q.top();Q.pop();
        ll con=s[u.l]*s[u.r]%mod;
        if(u.l<NN) Q.push((pls){u.l+1,u.r});
        ans=(ans+con)%mod;
    }
}

int main()
{
    scanf("%lld%d",&n,&k);
    pw[0]=1;
    for(int i=1;i<=13;i++)pw[i]=pw[i-1]*10ll;
    dfs(1,0,1);
    sort(nu.begin(),nu.end());
    for(unsigned int i=0;i<nu.size();i++){
        if(!mp[nu[i]]){ mp[nu[i]]=++tot; numb[tot]=nu[i]; }
        else continue;
    }
    for(int i=1;i<=12;i++){
        if(pw[i-1]>=n)break;
        dp(i);
    }
    ccnntt();
    printf("%lld\n",ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值