数位dp

【数位DP】CF55D Beautiful numbers

纵然是瞬间 提交于 2019-11-30 13:36:31
$dp[x][p][pp]$表示第x位,当前已有数字mod 2520(1~9数字的lcm)为p,当前各位数字的lcm为pp 观察到数组太大,考虑压缩,第三维lcm最多只有9个数字,打表发现最多只有48个状态,压掉第三维即可 打表用一个状压然后set维护(广搜也可以)即可 有一个坑点:题目里似乎没有说关于0的事情(即数字里出现0)但是有人在CF上打这个比赛的时候问了出题人,碰到0不要管即可!!! 打表代码: 1 set<int>s; 2 inline void Make(int x){ 3 int ans=1; 4 for(int i=0;i<9;i++){ 5 if(((1<<i)&x)) ans=lcm(ans,i+1); 6 }s.insert(ans); 7 } 8 inline void States_Maker(){ 9 int ans=1; 10 for(int i=0;i<(1<<9);i++)Make(i); 11 while(!s.empty()){ 12 cout<<*s.begin()<<","; 13 s.erase(s.begin()); 14 } 15 } 解题代码: 1 #include<bits/stdc++.h> 2 #define int long long 3 #define writeln(x) write(x),puts("") 4

hdu3555 Bomb ——数位DP入门题

非 Y 不嫁゛ 提交于 2019-11-30 12:50:28
参考: http://www.cnblogs.com/liuxueyang/archive/2013/04/14/3020032.html 题意:给一个数字N,求1到N中含有49的数的个数 分析:经典数位dp。 首先定义状态: dp[i][0]代表长度为 i 并且不含有49的数字的个数; dp[i][1]代表长度为 i 并且不含有49,但是最高位是9的数字的个数; dp[i][2]代表长度为 i 并且含有49的数字的个数。 转移: dp[i][0] = dp[i-1][0] a[i] - dp[i-1][1]; 表示长度为 i 的不含有49的数字的个数等于长度为 i - 1 的不含有49的数字的个数 当前的数字,因为这个位置可以填0~a[i] - 1,然后再减去长度为 i - 1 的最高位是9的数字的个数,因为如果长度为 i - 1 的最高位是9的话,那么高一位就不能填4了,否则就组成了49。 dp[i][1] = dp[i-1][0]; 表示长度为 i 的并且不含有49同时最高位是9的数字的个数等于,长度为 i - 1 的不含有49的数字的个数,因为只要在它的高一位加上一个9就可以了。 dp[i][2] = dp[i-1][2] a[i] + dp[i-1][1]; 表示长度为 i 的含有49的数字的个数等于,长度为 i - 1 的数字的个数 当前的数字,再加上长度为 i - 1

数位DP(学习笔记)

浪尽此生 提交于 2019-11-30 12:18:09
洛咕日报这篇博客写得很好,蒟蒻在这里学会了 摘自上面那篇博客的模板:不过我一般不记录 \(st\) 这个值,而是在 \(pos>len\) 时直接返回1,表示找到了一个合法的数. ll dfs(int pos,int pre,int st,……,int lead,int limit){//记搜 if(pos>len) return st;//剪枝 if((dp[pos][pre][st]……[……]!=-1&&(!limit)&&(!lead))) return dp[pos][pre][st]……[……];//记录当前值 ll ret=0;//暂时记录当前方案数 int res=limit?a[len-pos+1]:9;//res当前位能取到的最大值 for(int i=0;i<=res;i++){ //有前导0并且当前位也是前导0 if((!i)&&lead) ret+=dfs(……,……,……,i==res&&limit); //有前导0但当前位不是前导0,当前位就是最高位 else if(i&&lead) ret+=dfs(……,……,……,i==res&&limit); else if(根据题意而定的判断) ret+=dfs(……,……,……,i==res&&limit); } if(!limit&&!lead) dp[pos][pre][st]……[……]=ret;/

HDU 4734 :F(x)【数位DP】

爷,独闯天下 提交于 2019-11-30 04:33:37
Discription For a decimal number x with n digits (A nA n-1A n-2 … A 2A 1), we define its weight as F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + … + A 2 * 2 + A 1 * 1. Now you are given two numbers A and B, please calculate how many numbers are there between 0 and B, inclusive, whose weight is no more than F(A). Input The first line has a number T (T <= 10000) , indicating the number of test cases. For each test case, there are two numbers A and B (0 <= A,B < 10 9) Output For every case,you should output "Case #t: " at first, without quotes. The t is the case number starting from 1. Then output the

TZOJ 2478 How many 0's?(数位DP)

女生的网名这么多〃 提交于 2019-11-30 01:36:12
描述 A Benedict monk No.16 writes down the decimal representations of all natural numbers between and including m and n , m ≤ n . How many 0's will he write down? 输入 Input consists of a sequence of lines. Each line contains two unsigned 32-bit integers m and n , m ≤ n . The last line of input has the value of m negative and this line should not be processed. 输出 For each line of input print one line of output with one integer number giving the number of 0's written down by the monk. 样例输入 10 11 100 200 0 500 1234567890 2345678901 0 4294967295 -1 -1 样例输出 1 22 92 987654304 3825876150 题意 求[l,r]之间0的个数

2019年9月训练(壹)数位DP (HDU 2089)

与世无争的帅哥 提交于 2019-11-29 19:19:40
开学之后完全没时间写博客.... HDU 2089 不要62(vjudge) 数位DP 思路: 题目给出区间[n,m] ,找出不含4或62的数的个数 用一个简单的差分:先求0~m+1的个数,再减去0~n的个数. 但问题依旧不简单,再次简化为求0~i位数中不含4或62的数的个数. i=1 //0~9中 i=2 //0~99中 i=3 //0~999中 ...... dp[i][0] //0~i位数中的吉利数 dp[i][1] //0~i位数中以2打头的吉利数 dp[i][2] //0~i位数中的非吉利数(含4或62) 所以第i位数中的吉利数个数为: dp[i][0]=dp[i-1][0]*9-dp[i-1][i] 第i位数中以2打头的幸运数个数为: dp[i][1]=dp[i-1][0] 第i位数中的非吉利数个数为: dp[i][2]=dp[i-1][2]*10+dp[i-1][0]+dp[i-1][1] 同时初始值为: dp[0][0]=1; dp[0][1]=0; dp[0][2]=0; AC码: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int dp[10][5]; void INIT() { memset(dp,0,sizeof(dp)); dp[0][0]=1;

洛谷 P2657 (数位DP)

ぐ巨炮叔叔 提交于 2019-11-29 03:14:29
### 洛谷 P2657 题目链接 ### 题目大意: 给你一个数的范围 [A,B] ,问你这段区间内,有几个数满足如下条件: 1、两个相邻数位上的数的差值至少为 2 。 2、不包含前导零。 很简单的数位DP,可想只需标记前导零 lead, 前一个数 pre ,即可暴力统计答案,再记忆化就行了,但是有些地方还要细心一点。 比如在枚举到第一个有效位时(即非前导零),它当前只有一个数,而我们需要设 q = true (q 表示枚举到当前位时,是否满足条件,即相邻位之差是否达到 2 )。即我需要保证枚举到第二个有效数位时,要与第一个有效数位作差值比较的话,那么在枚举第一个有效位时,不能使得 q == false。 然后根据样例 1 可以知道,个位数也算。那么为了使第一位满足 abs(i - pre)>= 2 的话,那么我们需要使得一开始 pre == -1 即可,因为 i 最少会为 1 。 代码如下: 根据条件枚举数位 #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; int A,B; int a[12],dp[12][12]; ll dfs(int pos,int pre,bool lead,bool limit){ if(pos==0

数位DP

杀马特。学长 韩版系。学妹 提交于 2019-11-28 23:07:33
问题:求某个区间[L,R]中满足某种限制条件的数的个数。 暴力的思路:我们可以枚举[L,R]区间内部所有的数,依次判断每个数是否合法,若合法,就记下。 显然,如果数据范围较大,达到10 18 甚至更大的级别的时候,这种暴力的算法无论时间还是空间都无法 解决此类问题,所以我们引入数位DP。 所谓数位DP,就是换一种枚举的方式,这种枚举的方式是基于数位(个位,百位,千位...)进行的,与 数的大小没有任何关系。 一般记忆化搜索版本要比数组递推版好实现。 从一道题来引入 (HDU2089)不要62 题意:给定区间,求区间内部不含连续的62和4的数。 数据范围 l,r<=10000000; 我们考虑从最高位开始一位一位的进行枚举,运用记忆化搜索,如果枚举到最后一位所有位都合法,我们就找到了一个数。 思考,前一位什么情况会对后一位的枚举产生影响: 1,这位的前一位是6,这一位枚举了2,那么后面再枚举的情况都不再合法,我们去掉这种情况 2,这一位枚举到4,后面枚举的情况都不合法,我们去掉这种情况。 3,这一位枚举到了范围的最高位,那么下一位枚举的范围不能是0-9,而只能是0-这位的边界。 *在数位DP的记忆化搜索中,记忆化的是穷举0-9的情况,最高位单独枚举并不会产生太大的复杂度。 *数位DP运用到了前缀和的思想,我们统计1-R的答案,在统计1-L-1的答案,相减即为答案。

洛谷 P4124 (数位 DP)

本秂侑毒 提交于 2019-11-28 22:27:32
### 洛谷 P4124 题目链接 ### 题目大意: 给你一段区间,让你求满足下列两个条件时的数的个数。 1、至少有 3 个相邻相同数字 (即 111 、1111 、222 等) 2、不能同时出现 8 与 4 。 给定的区段 [L,R] 中 ,L 与 R 的数值范围为:10 10 ≤ L ≤ R < 10 11 分析: 1、用 q 与 w 标记 8 是否出现 ,4 是否出现。 2、为了得知是否有连续三个相等,故还需要 ppre 表示前两位中的第一位数位上的数,pre 表示前一位数位上的数,还需要再加 flag 标记是否当前已满足 ppre == pre == i 3、剩下的就是前导零与位数限制标记了 (即 lead 与 limit ) 故 dp 需要 5 维,pos、ppre、pre、flag、q、w ,然后直接记忆化即可。 代码如下: #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; ll L,R; ll a[15]; ll dp[15][15][15][2][2][2]; ll dfs(int pos,int ppre,int pre,bool flag,bool q,bool w,bool lead,bool limit){

洛谷 P2602(数位DP)

旧时模样 提交于 2019-11-28 22:22:11
### 洛谷 P2602 题目链接 ### 题目大意: 给你一个区间,问你区间所有数字中,0、1、2 .... 9 的个数的总和分别为多少。 分析: 枚举 0 ~ 9 进行数位 DP 即可。 注意记忆化搜索: 必须要用到第二维来表示,前 1 ~ pos 位,某个数(0 ~ 9)的个数。 例如,我们在求这个区间中 2 的个数,直接看的话,后 pos 位 的 2 的个数好像与 1 ~ pos位 上有多少个 2 并无联系(在 !limit 情况下),那为什么还要开第二维呢? 实际算上来你会发现:比如当枚举到 222 这个数时,很显然 pos==0 后,2 的总个数(即 sum)为 3 。而如果我们枚举到 第 2 位 (十位上)时,用到的记忆化是 dp[2] ,它只记录的是后两位中,有 22 这个数,即两个 2 ,而实际我们求 222 时,应该使程序返回的是 sum== 3 。故我们需要记忆化 dp[2][1] ,让在前 1 ~ pos 位已有一个 2 的时候,返回 sum== 3 (即这个 1 最后会再加上后面两个 2 的个数 ,对应的是 22 ) 代码如下: #include<iostream> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; ll n,m; int a