[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;
}

时间: 2024-08-02 14:45:42

[BZOJ 3131][Sdoi2013]淘金的相关文章

Bzoj 3131 [Sdoi2013]淘金 题解

3131: [Sdoi2013]淘金 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 733  Solved: 363[Submit][Status][Discuss] Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块.    一阵风吹过,金子的位置发生了一些变化.细心的小Z发现,初始在(i,j)坐标处的金子会变到(f(i),

BZOJ 3131 SDOI2013 淘金 数位dp

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3131 题意没什么好概述的..... 首先从题目对数的每一位进行相乘的操作以及N的范围可以看出来估计和数位dp有关系. 先考虑一维的情况.可以发现一个数的每一位相乘得到的数字质因数只有2,3,5,7,并且带有0的数字是没有贡献的,同时我们可以简单证明对于题目中的函数f(x)一定有f(x)<x,也即是说所有的f(x)都是在向1靠拢,具体到哪里取决于这个数的质因数. 于是可以令f(i,a,b,

bzoj 3198: [Sdoi2013]spring 题解

[原题] 3198: [Sdoi2013]spring Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 253  Solved: 95 Description Input Output Sample Input 3 3 1 2 3 4 5 6 1 2 3 0 0 0 0 0 0 4 5 6 Sample Output 2 HINT [题解]这道题明明是水题,坑了我两天!!!真是伤心.发现哈希都不熟练了. 首先很容易想到是2^6枚举01状态,使得1

bzoj3131 [Sdoi2013]淘金

[Sdoi2013]淘金 Time Limit: 30 Sec  Memory Limit: 256 MB 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.如果

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,存不下,怎么办 我

淘金(bzoj 3131)

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的范围内,我们认为这块金子已经被移出游戏.同时可以发现,对于变化之后的游戏局面,

BZOJ 3131 淘金

考虑到乘出来的东西实际上不多...直接map记下. 然后比如说x位的计数,就把ceil(x/2)和trunc(x/2)的情况乘起来. 然后就是一个ai,j=tab[i]*tab[j],求这个数表前k大的问题. 这个可以排序,然后单调队列,把状态慢慢往后推就行了.(注意到ai,j都不用取模才可以这么做,否则...我也不知道怎么做) #include<iostream> #include<cstdio> #include<cstring> #include<algor

[BZOJ 3129] [Sdoi2013] 方程 【容斥+组合数取模+中国剩余定理】

题目链接:BZOJ - 3129 题目分析 使用隔板法的思想,如果没有任何限制条件,那么方案数就是 C(m - 1, n - 1). 如果有一个限制条件是 xi >= Ai ,那么我们就可以将 m 减去 Ai - 1 ,相当于将这一部分固定分给 xi,就转化为无限制的情况了. 如果有一些限制条件是 xi <= Ai 呢?直接来求就不行了,但是注意到这样的限制不超过 8 个,我们可以使用容斥原理来求. 考虑容斥:考虑哪些限制条件被违反了,也就是说,有哪些限制为 xi <= Ai 却是 xi

BZOJ 3129 [Sdoi2013]方程 不定方程解的个数+组合数取模

题意:链接 方法:不定方程解的个数+组合数取模 解析: 先看n1与n2的部分的限制. 对于后半部分的限制来说,我们直接减去An1+i?1就可以转化一下求正整数解. 但是前半部分呢? 跟上一道猴子那个很像. 所以我们容斥搞就行了. 但是这道题好像不好写的地方不在这? 这题TMD不就是礼物吗! 大组合数取模如何取? 请参见我<BZOJ 礼物>的题解. 另外吐槽题干 明明是X1+X2+-+Xn=m 并不是小于等于 代码: #include <cstdio> #include <cs