数位dp

牛客多校第七场H Pair 数位dp理解

五迷三道 提交于 2019-11-28 18:42:42
Pair 题意 给出A B C,问x取值[1,A]和y取值[1,B]存在多少组pair<x,y>满足以下最小一种条件, \(x \& y >c\) , \(x\) xor \(y<c\) 分析 有关二进制位运算的操作肯定是和要联想到和位的关系的,我们可以考虑枚举每一位计数,但这样会复杂度爆炸,枚举每一位有没有想到什么?数位dp,我们可以考虑把题目条件装化,全集好求,那么求他的补集,求所有 \(x \& y <=c\) 并且 \(x\) xor \(y>=c\) ,然后用全集A×B减去就是答案了。 这里的数位dp状态为dp[位数][A枚举上界][B枚举上界][是否满足x and y< c ][是否满足x xor y>c][A是否取了不为0的数][B是否取了不为0的数] 这里有一个关键点,状态中[是否满足x and y< c ][是否满足x xor y>c] 没有取等号,为什么不取等号呢,因为如果条件相反大于的时候我们会直接continue,那么状态就只剩下了到目前位置等于c,和不等于c的相应条件了,这是两种不同的状态,但是都是合法的,所以要记录下来。 刚开始写的时候状态为dp[位数][是否满足x and y< c ][是否满足x xor y>c][A是否取了不为0的数][B是否取了不为0的数] ,即把两个limit没有放到状态里面,就T了。因为常规的写法数位dp是求两个区间之间的值

数位dp CodeForces - 55D 美丽数字

痞子三分冷 提交于 2019-11-28 16:44:11
Beautiful numbers CodeForces - 55D 题意:定义能被自己所有位数整除的数字为美丽,给定一个区间,求区间内的美丽数字个数。 分析:首先,可以把限制条件转化为之前所有位数的 最大公倍数 ,将pos,sum,lcm,up当作 dfs的条件和dp下标,然后 dp[ pos ][ sum ][ lca ][ up ] 就代表着 pos 之后的位置 全部遍历完后,该 状态取这个sum的最大值 。这里要避免一个问题,就是 15 和 12,不可以从 15 的 dp[ pos=1 ][ sum=0 ][ up=1 ] 的记忆直接得到 12 的 dp[ pos=1 ][ sum=0 ][ up=1 ] ,而数位dp又需要这样的记忆化来减少时间复杂度,因此,这里的 up 就有了两个作用,即判断某个位置可以遍历到数字几 和 将15 的dp[ 1 ] 定义为 饱和 (避开15的dp[1] 和 12的dp[1],而选择15的dp[0] 来记忆退得 12的dp[0] ),那么利用记忆化的时候使用的是 dp[ 0 ] = 10 , 即up=0的上一位,就完美解决了。 并且这样就可以用memset一次,别的都可以用记忆化,大大减少时间复杂度。 然后发现MLE了,原因在于数组开太大 ,而1~9的公倍数为2520,[ sum ]的最大值就可以看成 2520,大的就用取模(有相关数论)

数位dp HDU - 2089 不要62

放肆的年华 提交于 2019-11-28 16:40:40
https://cn.vjudge.net/pr oblem/HDU-2089 题意: 题意:给定一个区间,求区间中不包含4、连续的62的数字有几个。 分析:基础的数位dp,利用开多维的dp数组和 dfs 的多个参数来实现各种限制条件,从高位到低位dfs,并且多用 if 判断,结尾 return 1。 ( 用dig[] 保存数位,ans 基本框架为 sol(m) - sol(n - 1) ) #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 30; int dig[maxn]; int dp[maxn][2][2]; int dfs(int pos, int six, int flag) { if (pos < 0) return 1; if (dp[pos][six][flag] != -1) return dp[pos][six][flag]; int res = 0; for (int i = 0; i <= 9; i++) { if (six && i == 2) continue; if ( i == 4) continue; if (flag && i > dig[pos]) continue;

HDU 6148 (数位DP)

北城余情 提交于 2019-11-28 15:49:58
### HDU 6148 题目链接 ### 题目大意: 众所周知,度度熊非常喜欢数字。 它最近发明了一种新的数字:Valley Number,像山谷一样的数字。 当一个数字,从左到右依次看过去数字没有出现先递增接着递减的“山峰”现象,就被称作 Valley Number。它可以递增,也可以递减,还可以先递减再递增。在递增或递减的过程中可以出现相等的情况。 比如,1,10,12,212,32122都是 Valley Number。 121,12331,21212则不是。 度度熊想知道不大于N的Valley Number数有多少。 注意,前导0是不合法的。 分析: 1、数位DP,很容易知道第一维表示的是 数位 ,第二维表示的是前一个数,而由于要记录斜率(即递增递减),所以要加入第三维,表示在当前数位之前,是递增还是递减或是没有(当且仅当有两个不同数时,才会有递增或递减) 2、前导零需要加入 lead 标记,然后根据样例,这题好像数字 0 也被认定为了前导零,故不需要加以判断为 0 的情况。 3、然后根据题意,如果该数位之前满足递增且当前数严格小于上一个数时,才 continue。 4、注意 lead==true 时,也是没有递增递减的,因为直到第一个非 0 数才开始计算。即当如果 lead==true 而此时数位上不为 0 时,则说明枚举的数字从该位开始,故可以与 pre==-1

POJ 3252 (数位DP)

心已入冬 提交于 2019-11-28 15:39:36
###POJ 3252 题目链接 ### 题目大意: 给你一段区间 [Start,Finish] ,在这段区间中有多少个数的二进制表示下,0 的个数 大于等于 1 的个数。 分析: 1、很显然是数位DP,枚举这区间中所有数的二进制位数。由于与 0 的个数有关,故需要用 lead 标记前导零情况。 2、然后就是要处理 1 的个数与 0 的个数,故 dp 的第二维状态即要表示出枚举到当前位 pos 时所拥有的 0 的个数 (或 1)。 但是你会发现,如果当前知道的是 前面几位中 0 的个数,为了满足题意,我还需要知道前面几位中 1 的个数,然后求差值,再到后面 pos 位中找相应的 dp 值。故为了简便,第二维设的应该是当前 1~pos位中,1的个数减去 0 的个数的差值。由于差值可能为负数,且 20亿 最高不会超过 32 位(二进制),故取 32 为中间值,然后根据枚举位上的 1 或 0 来加减。 故 dp[i][j] 表示,在 1~ i 位中,1 与 0 的个数差值为 j (与 32 的差值)。 本题的记忆化搜索: 当从最高位枚举到第 pos 位时,第 1 ~ pos 位上与当前状态对应于 dp[pos][差值] 时满足条件的个数,这个差值来源于 最高位到 pos 位 。比如从最高位的前三位都为 1 ,那么此时从最高位枚举了 3 位,且sum = 32 + 3 = 35 ,那么

模板 - 数位dp

此生再无相见时 提交于 2019-11-28 05:26:35
#include<bits/stdc++.h> using namespace std; #define ll long long int a[20]; ll dp[20][MAXS1][MAXS2]; ll dfs(int pos,int s1,int s2,bool lead,bool limit) { if(pos==-1) { return ?; } if(!limit && !lead && dp[pos][s1][s2]!=-1) return dp[pos][s1][s2]; int up=limit?a[pos]:9; ll ans=0; for(int i=0; i<=up; i++) { int ns1=op1(s1); int ns2=op2(s2); ans+=dfs(pos-1,ns1,ns2,lead && i==0,limit && i==a[pos]); } if(!limit && !lead) dp[pos][s1][s2]=ans; return ans; } ll solve(ll x) { if(x<=0) return ?; int pos=0; while(x) { a[pos++]=x%10; x/=10; } return dfs(pos-1,INITS1,INITS2,true,true); } int main() {

数位dp

六月ゝ 毕业季﹏ 提交于 2019-11-28 03:49:14
基础 对于一类问题:求给定区间内,满足给定条件的数的个数。一般情况下,这类问题通常采用暴力枚举求解: for(int i=l;i<=r;++i) if(right(i)) ++ans; 很显然,当给定区间过大时,无法直接用朴素的方法求解。而所求的限定条件往往与数位有关,例如数位之和、指定数码个数、数的大小顺序分组等等。此时我们就需要利用数位的性质,设计log级别复杂度的算法。 解决这类问题最基本的思想就是“逐位确定”,有时求解这类问题时还需要做预处理,而预处理的过程即可视作数位dp。 简单而言,数位dp就是在给定的区间内,利用限定条件,逐位暴力枚举的算法。 对于数位dp,可通过简单的分析进行了解。 我们假设给定一个r=312。对于数位dp的过程,也就是从最高位即百位起,顺次枚举0-9。 但是很显然,以百位3为例,枚举时数字最大不能超过3。同时当百位为3时,十位最大不能超过1;当百位为3,十位为1时,个位最大不能超过2。其他限定情况下也以此类推。所以在实际的运用中,需要用一个布尔变量limit记录这一情况。 另一方面,数位dp中,对于问题的求解,通常是: printf("%d\n",solve(l)-solve(r-1)); 所以不管左端点是多少,百位一定会枚举到0,可是这样前导零可能会影响计数(视题目而定),所以通常情况下还会用一个布尔变量lead记录具体情况。

数位dp

别说谁变了你拦得住时间么 提交于 2019-11-27 20:32:28
在信息学竞赛中,有一类与数位有关的区间统计问题。这类问题往往具有比较浓厚的数学味道,无法暴力求解,需要在数位上进行递推等操作——刘聪《浅谈数位类统计问题》 例题1:[bzoj1833]数字计数(模板题)    题意: 求区间内0-9各出现了几次   1.做一个差分,[a,b]的统计可以理解为[1,b]-[1,a)的统计,问题转化为求前缀的0-9个数   2.要对0-9每一个数字分别dp(也可以dp多加一维状态),问题转化为求前缀的1个数(以1为例)   3.数位dp+记忆化搜索,记录五个状态(具体见代码)(还有一种dp预处理+乱搞计算,比较复杂 其实是我太菜了 ) 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 int a[21]; 5 ll x,y,f[21][11][21];//f[i][j][k]表示之前已经有k个j,最后i位任意填,一共有几个j(本题中可以优化,但大部分题目不能) 6 ll dfs(int k,int t,int s,int p1,int p2){//k表示dp到第i位(从高到低),t表示统计t,s表示已经有多少t,p1表示是否有非0数,p2表示是否能取到上限 7 if (!k)return s;//dp完成,返回t的个数 8 if ((p1)&&(p2)&&

洛谷4317花神的数论题(数位DP)

ぐ巨炮叔叔 提交于 2019-11-27 16:45:16
传送门 本来是想刷数论题的,万万没想到的是,这道题题目是叫数论题,但其实它是道数位DP呢。 既然sum ( i ) 表示 i 的二进制表示中 1 的个数,而数据范围又很大,是1e15,暴力肯定是不行的。 但我们知道,肯定有很多数二进制的1的个数是一样的,考虑可不可以把问题转化成对于每一个k,找出二进制里有k个1的数有多少个。 我们发现把N换为2进制的话,也不过就是50位的样子。那么最多也才50个1,那么k从 1 for到 50,每个k找一遍,把个数存一下。 问题转化为:对于一个区间的数,找出二进制中有k个1的数有多少个----->数位dp。 #include<bits/stdc++.h> #define LL long long #define mod 10000007 using namespace std; LL read() { LL x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } int num[52]; LL dp[52][52][52][3],f[52];//小心爆int int tot=0; LL dfs(int pos,int cnt,int

HDU -2089 不要62(数位DP×)

南笙酒味 提交于 2019-11-27 14:58:10
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。 不吉利的数字为所有含有4或62的号码。例如: 62315 73418 88914 都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。 你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。 Input 输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。 Output 对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。 Sample Input 1 100 0 0 Sample Output 80 数位dp不太会,这个方法只适合数据量小的情况,应付这一题可以 # include <iostream> # include <cstdio> # include <algorithm> # include <cstring>