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,c,d,0/1)表示当前考虑第i位(这次我从高到低实现的),已经考虑的数中有多少个的数位乘积为2^a * 3^b * 5^c * 7^d,有无限制。

方程很好写,注意特判一下当前位数不是N的位数的时候最高位的情况,注意初始化状态是有限制的。查找答案直接暴力扫一遍就可以了。

二维?可以发现由于这是个正方形,行列信息实际上本质是一样的,同时不难发现格子(x,y)中的金子数量最终是f(0,x的质因数分解)*f(0,y的质因数分解)。

再一想,这是个多路归并!

上一个优先队列,问题圆满解决。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 typedef unsigned long long LL;
14 const int mo=1000000007;
15
16 LL N,K,f[15][40][27][14][14][2],A[40*27*14*14];
17 int cnt;
18 struct data{ int p1,p2; LL v; };
19 struct cmp{
20     bool operator () (data x,data y){
21         return x.v<y.v;
22     }
23 };
24 priority_queue<data,vector<data>,cmp>pq;
25
26 void dev(int x,int *t)
27 {
28     if(!x) return;
29     while(x!=1&&x%2==0) x/=2,t[0]++;
30     while(x!=1&&x%3==0) x/=3,t[1]++;
31     while(x!=1&&x%5==0) x/=5,t[2]++;
32     while(x!=1&&x%7==0) x/=7,t[3]++;
33 }
34 void dp()
35 {
36     int up=0,n[20]={0},tt[10][4]={0}; LL tmp=N;
37     n[up++]=tmp%10,tmp/=10;
38     while(tmp) n[up++]=tmp%10,tmp/=10;
39     for(int i=0;i<10;i++) dev(i,tt[i]);
40     f[up][0][0][0][0][1]=1;
41     for(int i=up-1;i>=0;i--)
42     for(int x=1;x<10;x++){
43         int *t=tt[x];
44         for(int p1=t[0];p1<=(up-i)*3;p1++)
45         for(int p2=t[1];p2<=(up-i)*2;p2++)
46         for(int p3=t[2];p3<=up-i;p3++)
47         for(int p4=t[3];p4<=up-i;p4++){
48             f[i][p1][p2][p3][p4][0]+=f[i+1][p1-t[0]][p2-t[1]][p3-t[2]][p4-t[3]][0];
49             if(x<n[i]) f[i][p1][p2][p3][p4][0]+=f[i+1][p1-t[0]][p2-t[1]][p3-t[2]][p4-t[3]][1];
50             if(x==n[i]) f[i][p1][p2][p3][p4][1]+=f[i+1][p1-t[0]][p2-t[1]][p3-t[2]][p4-t[3]][1];
51         }
52         if(i!=up-1) f[i][t[0]][t[1]][t[2]][t[3]][0]++;
53     }
54 }
55 void work()
56 {
57     cin>>N>>K;
58     dp();
59     for(int p1=0;p1<37;p1++)
60     for(int p2=0;p2<25;p2++)
61     for(int p3=0;p3<13;p3++)
62     for(int p4=0;p4<13;p4++){
63         if(f[0][p1][p2][p3][p4][0]+f[0][p1][p2][p3][p4][1])
64             A[cnt++]=f[0][p1][p2][p3][p4][0]+f[0][p1][p2][p3][p4][1];
65     }
66     sort(A,A+cnt);
67     for(int i=0;i<cnt;i++)
68         pq.push((data){i,cnt-1,A[i]*A[cnt-1]});
69     data t;
70     int ans=0;
71     for(int k=1;k<=K;k++){
72         if(pq.empty()) break;
73         t=pq.top(); pq.pop();
74         ans=(ans+t.v%mo)%mo;
75         if(t.p2) pq.push((data){t.p1,t.p2-1,A[t.p1]*A[t.p2-1]});
76     }
77     printf("%d\n",ans);
78 }
79 int main()
80 {
81     work();
82     return 0;
83 }

原文地址:https://www.cnblogs.com/KKKorange/p/8575758.html

时间: 2024-08-28 14:43:47

BZOJ 3131 SDOI2013 淘金 数位dp的相关文章

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 1799 同类分布(数位DP)

给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数.1<=a<=b<=1e18. 注意到各位数字之和最大是153.考虑枚举这个东西.那么需要统计的是[0,a-1]和[0,b]内各位数字之和为x且能整除x的数字个数. 那么我们只需要数位dp一波即可. 令dp[pos][i][x]表示有pos位且数字之和为x的数mod P=i的数字个数. 则转移方程显然可得. # include <cstdio> # include <cstring> # include

BZOJ 1833 数字计数(数位DP)

经典数位DP模板题. # include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath>

bzoj 1026 windy数(数位DP)

1026: [SCOI2009]windy数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 2615  Solved: 1155 [Submit][Status] Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? Input 包含两个整数,A B. Output 一个整数. Sample Input [

[BZOJ 1833] [ZJOI2010] count 数字计数 【数位DP】

题目链接:BZOJ - 1833 题目分析 数位DP .. 用 f[i][j][k] 表示第 i 位是 j 的 i 位数共有多少个数码 k . 然后差分询问...Get()中注意一下,如果固定了第 i 位,这一位是 t ,那么数码 t 的答案是要加一个值的(见代码). 代码 #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <cmat

[您有新的未分配科技点]数位DP:从板子到基础

只会统计数位个数或者某种"符合简单规律"的数并不够--我们需要更多的套路和应用 数位dp中常用的思想是"分类讨论"思想.下面我们就看一道典型的分类讨论例题 1026: [SCOI2009]windy数 Time Limit: 1 Sec  Memory Limit: 162 MB Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个wind

【数位DP】

[总览] 通过将数拆分成一位位的进行dp(记忆化搜索),状态最基本的有:位置($pos$),最高位限制($limit$),前导零($lead$),前一位($pre$)等等,通常需要的状态视题目而定. 记忆化搜索的数组$dp$由多维构成,每一位都是一种状态的因素. [bzoj]不要62 数位$dp$入门题,$dp$数组为$dp[pos][pre][limit]$($limit$可以通过更改代码去掉这一维). [code] #include<iostream> #include<cstdio

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圆环数(数位DP+恶心细节)

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圆环数 Time Limit: 5 Sec  Memory Limit: 64 MB Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺序.她们甚至也不能通过仍硬币的方式. 所以她们通过"round number"竞赛的方式.第一头牛选取一个整数,小于20亿.第二头牛也这样选取一个整数.如果这两个数都是 "round numbers"