淘金(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的范围内,我们认为这块金子已经被移出游戏。同时可以发现,对于变化之后的游戏局面,某些坐标上的金子数量可能不止一块,而另外一些坐标上可能已经没有金子。这次变化之后,游戏将不会再对金子的位置和数量进行改变,玩家可以开始进行采集工作。
    小Z很懒,打算只进行K次采集。每次采集可以得到某一个坐标上的所有金子,采集之后,该坐标上的金子数变为0。
    现在小Z希望知道,对于变化之后的游戏局面,在采集次数为K的前提下,最多可以采集到多少块金子?
    答案可能很大,小Z希望得到对1000000007(10^9+7)取模之后的答案。

Input

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

Output

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

Sample Input

1 2 5

Sample Output

18

HINT

N < = 10^12 ,K < = 100000

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

/*
  数位DP+堆
  这个题比较显然的做法是求出一维的转移,然后再用堆求出二维的前K大,问题是数据太大,无法直接求一维的。
  我们可以发现,最大的积在10000左右,所以可以先dfs出所有可能的积,然后用数位DP求出积的个数。
  f[i][j][k]表示填了i为,积为j,下一维能否任意填数的方案数。
*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<queue>
#define mod 1000000007
#define lon long long
#define N 400010
using namespace std;
lon n,k,a[N],len,dis[N],cnt,size[N],num[N],f[15][N][2];
struct node{
    lon x,y,val;
    node(lon a,lon b){x=a;y=b;val=size[num[a]]*size[num[b]];}
    bool operator<(node s1) const{
        return val<s1.val;
    }
};priority_queue<node> q;
lon read(){
    lon num=0,flag=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)flag=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){num=num*10+c-‘0‘;c=getchar();}
    return num*flag;
}
bool cmp(int a,int b){return size[a]>size[b];}
void dfs(lon last,lon dep,lon now){
    if(dep==len+1){
        dis[++cnt]=now;
        return;
    }
    if(!now) return;
    for(lon i=last;i<10;i++)
        dfs(i,dep+1,now*i);
}
int main(){
    n=read();k=read();
    while(n){
        a[++len]=n%10;
        n/=10;
    }
    dfs(0,0,1);
    dis[++cnt]=0;
    sort(dis+1,dis+cnt+1);
    cnt=unique(dis+1,dis+cnt+1)-dis-1;
    f[0][2][0]=1;
    for(lon i=0;i<=len;i++)
        for(lon j=1;j<=cnt;j++)
            for(lon l=0;l<2;l++)
                if(f[i][j][l]){
                    lon p=(i==0?0:1);
                    for(;p<10;p++){
                        f[i+1][lower_bound(dis+1,dis+cnt+1,dis[j]*p)-dis][l+p>a[i+1]]+=f[i][j][l];
                    }
                }
    for(lon i=1;i<=cnt;i++){
        num[i]=i;
        for(lon j=1;j<len;j++)
            size[i]+=f[j][i][0]+f[j][i][1];
        size[i]+=f[len][i][0];
    }
    sort(num+2,num+cnt+1,cmp);
    q.push(node(2,2));
    lon i=0,ans=0;
    while(!q.empty()){
        node temp=q.top();
        q.pop();
        ans=(ans+temp.val)%mod;
        i++;
        if(i>=k) break;
        if(temp.x!=temp.y){
            ans=(ans+temp.val)%mod;
            i++;
            if(i>=k) break;
        }
        if(temp.x!=temp.y) q.push(node(temp.x+1,temp.y));
        if(temp.x==2) q.push(node(temp.x,temp.y+1));
    }
    cout<<ans;
    return 0;
}
时间: 2024-10-10 18:58:50

淘金(bzoj 3131)的相关文章

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]淘金

题目连接:http://www.lydsy.com/JudgeOnline/problem.php?id=3131 首先可以想到先算出一维的,再拓展到二维. 我们设g[i]表示各位数字乘积为i的数字个数(<=n),然后可以用堆/优先队列算出二维的前k大. 现在关键是如何求g[i]. 我一开始也不会~\(≧▽≦)/~啦啦啦,但实际上所有乘积总个数只有2e5多(我也不知道为什么有题解说是1e4...可能是我错了吧),将乘积离散化,然后就可以进行数位DP了.具体如下:设f[i][j][k]表示当前算到

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 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千题计划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 1013: [JSOI2008]球形空间产生器sphere

二次联通门 : BZOJ 1013: [JSOI2008]球形空间产生器sphere /* BZOJ 1013: [JSOI2008]球形空间产生器sphere 高斯消元 QAQ SB的我也能终于能秒题了啊 设球心的坐标为(x,y,z...) 那么就可以列n+1个方程,化化式子高斯消元即可 */ #include <cstdio> #include <iostream> #include <cstring> #define rg register #define Max

bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛

对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b). Input 第一行一个数T,表示询问数. 接下来T行,每行两个数a,b,表示一个询问. Output 对于每一个询问,输出一行一个非负整数作为回答. Sample Input 4 7558588 9653114 6514903 445

【BZOJ】[HNOI2009]有趣的数列

[算法]Catalan数 [题解] 学了卡特兰数就会啦>_<! 因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一. 那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢. 将做偶数标为-1,做奇数标为+1,显然当偶数多于奇数时不合法,因为它压不住后面的奇数. 然后其实这种题目,打表就可知啦--QAQ 然后问题就是求1/(n+1)*C(2n,n)%p了,p不一定是素数. 参考bzoj礼物的解法. 看到网上清一色的素数筛+分解质因数解法,不解了好久,感觉写了假的礼物-- 后来觉得礼物的做法才比

洛谷 P2709 BZOJ 3781 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3