[Bzoj3131][Sdoi2013]淘金(数位dp)(优先队列)

3131: [Sdoi2013]淘金


 

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 847  Solved: 423
[Submit][Status][Discuss]

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)取模之后的答案。

Input


 

  共一行,包含两介正整数N,K。

Output


 

  一个整数,表示最多可以采集到的金子数量。

Sample Input


 

12 5

 


Sample Output


 

18

 

HINT


 

 

N < = 10^12 ,K < = 100000

对于100%的测试数据:K < = N^2

 

题解:


 

发现题目变成了scoi2012某道题简化版。

可以很轻松愉快的发现如果是各位数字相乘,质因子只有2357,又限制最多只有12位,所以发现预处理出来可以得到数就一万多个。

于是我们就轻松愉快的处理出了,g[i][j],i位数乘积为j的数出现次数。

j很大,但数只有一万多个,hash走起。

好,数位dp转移也很明显了,不想太讲太多,f[i][j]   i位数内乘积为j的数出现次数。。

好了,发现转移完毕后我们要找所有数次数相乘的前k大。

暴力枚举所有数是10000^2的。

我们发现每个数都是从第一个数转移起走,那么我们对于10000多个数把它记录成三元组(i,j,k)当前价值为i,当前数位j,匹配数位k,每次选了当前三元组后

又加入(v[j] * v[k + 1],j,k + 1)放入优先队列即可

AC代码:


 

 
# include <iostream>
# include <cstdio>
# include <queue>
# include <algorithm>
using namespace std;
typedef long long LL;
const int N = 5e4 + 12;
const int M = 2e5 + 12;
const LL mod = 1e9 + 7;
LL g[15][N],state[N],s[N],n,K;int dt,hs[M],data[15],len;
struct node{
    LL val;int id;
    bool operator <(const node & other)const{return val > other.val;}
}a[N];
struct e{
    LL w;int id,Id;
    bool operator <(const e & other)const{return w < other.w;}
};
priority_queue<e>st;
void insert(LL s)
{
    int p = s % M;
    while(hs[p])
    {
        if(state[hs[p]] == s)return;
        p++;
        if(p == M)p = 0;
    }
    hs[p] = ++dt;state[hs[p]] = s;
}
int id(LL s)
{
    int p = s % M;
    while(hs[p])
    {
        if(state[hs[p]] == s)return hs[p];
        p++;
        if(p == M)p = 0;
    }
    return 0;
}
void init()
{
    for(int i = 1;i <= 9;i++)
    {
        insert(i);
        g[1][id(i)] = 1;
    }
    for(int i = 1;i <= 13;i++)
    {
        int tmp = dt;
        for(int j = 1;j <= tmp;j++)
        {
            LL x = state[j];int v = j,u;
            for(LL k = 1;k <= 9;k++)
            {
                insert(x * k);
                u = id(x * k);
                g[i + 1][u] += g[i][v];
            }
        }
    }
}
LL dfs(int now,LL K,bool lim,bool first)
{
    if(!now)return K == 1 && !first;
    if(!lim && !first)return g[now][id(K)];
    LL ret = 0;int p = lim ? data[now] : 9;
    if(first)ret += dfs(now - 1,K,false,first);
    for(LL i = 1;i <= p;i++)if(!(K % i))
    ret += dfs(now - 1,K / i,lim && i == p,false);
    return ret;
}
void calc(LL k)
{
    len = 0;
    while(k)data[++len] = k % 10,k /= 10;
    for(int i = 1;i <= dt;i++)a[i] = (node){dfs(len,state[i],true,true),i};
    sort(a + 1,a + dt + 1);
    for(int i = 1;i <= dt;i++)if(a[i].val)
    st.push((e){a[i].val * a[1].val,i,1});
}
int main()
{
    init();
    scanf("%lld %lld",&n,&K);
    calc(n);LL ans = 0,ret;int u,v;
    while(K && !st.empty())
    {
        (ans += st.top().w) %= mod;u = st.top().id;v = st.top().Id + 1;
        st.pop();
        if(!a[v].val)continue;
        st.push((e){a[u].val * a[v].val,u,v});
        K--;
    }
    printf("%lld\n",ans);
}

 

 

 

转载于:https://www.cnblogs.com/lzdhydzzh/p/8636915.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值