数位dp

[HDU4867]Xor (线段树分治+类数位dp)

烂漫一生 提交于 2019-12-04 04:48:40
[HDU4867]Xor (线段树分治+类数位dp) 提供一种 \((m+n) log a log m\) 带有常数约 \(\frac{1}{log n}\) 的算法 处理询问,将后来加入的数算进序列中,则每个数 \(a_i\) 都有一段出现的区间 \([L,R]\) 离线询问后,我们考虑用线段树分治将这些数加入到询问区间上 由于最多只有5000个询问,事实上这些数在线段树上覆盖的区间最多只有 \(10000logm\) 个,并且有着极其不满的常数(因为每个位置上的数都由多段区间组合而来,总长为 \(m\) ,或者你可以觉得我在放屁) 如果直接处理每个数的贡献,那么这个 \(dp\) 是 \(a*a\) 转移的 然而事实上我们存在一种 \(a*loga\) 的转移方法 对于一个数 \(x\) ,如果我们取 \(y \leq x\) 时,最高位为 \(0\) ,则后面的位均可以随便取 换句话说,对于每一个前 \(k\) 位相同的集合,它们都能够转移到它们之间的任何一个,可以直接累和 同样的,考虑在第 \(k\) 位出现一个 \(x\) 在该位为 \(1\) , \(y\) 为 \(0\) ,都具有类似的转移性质 最后写出来跟数位 \(dp\) 一个样子。。。 (真不行你可以试试某变换,但是我不会!) 这样的情况个数即这个数 \(1\) 位的个数,这样的个数期望情况下可以看做常数。。。

[数位DP]

允我心安 提交于 2019-12-03 15:02:55
转载: 数位dp总结 之 从入门到模板 基础篇 数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp咯。数位还算是比较好听的名字,数位的含义:一个数有个位、十位、百位、千位......数的每一位就是数位啦!之所以要引入数位的概念完全就是为了dp。数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,然后记忆化就可以了。 两种不同的枚举:对于一个求区间[le,ri]满足条件数的个数,最简单的暴力如下: 1 for(int i=le;i<=ri;i++) 2 if(right(i)) ans++; 然而这样枚举不方便记忆化,或者说根本无状态可言。 新的枚举:控制上界枚举,从最高位开始往下枚举,例如:ri=213,那么我们从百位开始枚举:百位可能的情况有0,1,2(觉得这里枚举0有问题的继续看) 然后每一位枚举都不能让枚举的这个数超过上界213(下界就是0或者1,这个次要),当百位枚举了1,那么十位枚举就是从0到9,因为百位1已经比上界2小了,后面数位枚举什么都不可能超过上界。所以问题就在于:当高位枚举刚好达到上界是,那么紧接着的一位枚举就有上界限制了。具体的这里如果百位枚举了2,那么十位的枚举情况就是0到1,如果前两位枚举了21,最后一位之是0到3

codeforces597 div2 F 数位dp

荒凉一梦 提交于 2019-12-03 11:51:49
codeforces597 div2 F 数位dp 题意: 求[L,R]中a&b==0的“对数” 思路: 一个典型的求“对数”的数位dp,对比普通的数位dp,共用一个pos,维护两个limit。剩下的就是“暴搜”了,当然注意去重,因为是求对数就不是简单的 \(ans_{R}-ans_{L-1}\) 了。还有要注意lim的状态也要保存,不然会超时。 代码: #include <bits/stdc++.h> using namespace std; #define X first #define Y second #define PB push_back #define LL long long #define pii pair<int,int> #define MEM(x,y) memset(x,y,sizeof(x)) #define bug(x) cout<<"debug "#x" is "<<x<<endl; #define FIO ios::sync_with_stdio(false); #define ALL(x) x.begin(),x.end() #define LOG 20 const int inf =1e9; const int maxn =3e5+7; const int mod = 1e9+7; LL dp[35][2][2]; LL solve(int L

11 .3 数位dp

帅比萌擦擦* 提交于 2019-12-03 10:05:57
数位dp是以数位上的关系为转移关系而进行的一种计数dp,题目基本类型是给定区间[l ,r] ,求l到r之间满足要求的数字的个数 . dp状态的转移方式通常是用 递归+记忆化搜索 ,转移顺序一般是由高数位转移到底数位 ,其中就是记忆化搜索保证了数位dp的高效率 例如千位2到百位转移要枚举0,1,2,3 ...(2000,2100,2200,2300...) ,而千位3也是同样的(3000,3100,3200,3300...),其进行的都是对三位数000~999的统计,所以低位统计过程只用进行一次就可将结果应用于所有高位状态上,减少了重复过程的进行. 结果的输出形式是 0~r 之间的dp 与 0~l之间的dp 进行相减 来求 l到r 之间的 dp. printf("%lld\n",solve(r)-solve(l-1)); 值得注意的点是边界 l和r 不能进行记忆化搜索 ,比如 dp[2][sta] 记录的是 000~999(三位数) 中满足条件的数字的个数 ,而对于l = 2250 ,其在2000之后的三位数只有 100~250 ,所以这时候如果直接记忆化返回 dp[2][sta] 就会出现多记. 有的题目对前导零有要求,有的没有,做的时候随机应变。 例题: HDU 2089 不要62 题解:转移状态很清晰明了的题目,主要通过此题了解 递归+记忆化的转移方式

windy数(数位dp)

匿名 (未验证) 提交于 2019-12-03 00:42:01
传送门 #include<stdio.h> #include<string.h> #include<math.h> #include<stdlib.h> #define maxn 20 long long dp[20][10];//dp[i][j]表示前面一个数为j的i位数的方案数 int bit[20]; long long dfs(int pos,int pre,int f,int lim) //pos当前枚举到的位置,pre记录pos位置前一个位置的数,因为当枚举到pos然后无前导0的时候要拿判断i和前一个数即pre的差是否>=2,如果f为1,即枚举第pos位数时有前导0,此时pre是无意义的。 { int newf,i;//newf用来记录枚举pos-1位置时有没有前导0 int up=lim?bit[pos]:9; if(pos==0) return 1; if(!lim&&(f==0)&&(dp[pos][pre]!=-1)) return dp[pos][pre]; long long ans=0; for(i=0;i<=up;i++) { if(f==1&&i==0) newf=1; else if(f!=1) newf=0; else if(f==1&&i!=0) newf=0; if(f==1)//有前导0的话,pre无效,pow位置的数可以在0~up随便取 {

数位DP

匿名 (未验证) 提交于 2019-12-02 23:56:01
1 int pos=0; 2 while(k) 3 { 4 num[++pos]=k%10; 5 k/=10; 6 } 1 inline int dfs(int pos,int pre,int sta,bool limit)//pos:位数,pre:前驱,sta前一位是否是6,若不是则可以记忆化,否则因 2 //这一位2不能取而不能记忆化 limit:判断当前位的枚举上限 3 { 4 if(pos==0) return 1;//表示找到了最低位仍合法证明找到了一个数 5 if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];//记忆化不是上限的0-9过程 6 int up,sum=0; 7 if(!limit) up=9; else up=num[pos];//判断这位的枚举上限 8 for(int i=0;i<=up;i++)//枚举这一位的所有可能 9 { 10 if(i==4) continue;//去掉4的情况 11 if(pre==6&&i==2) continue;//去掉前驱是6,这位是2的情况 12 sum+=dfs(pos-1,i,i==6,limit&&(i==num[pos]));//搜 13 } 14 if(!limit) dp[pos][sta]=sum; 15 return sum; 16 } #include

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

匿名 (未验证) 提交于 2019-12-02 23:55:01
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-12-02 23:55:01
题意:定义能被自己所有位数整除的数字为美丽,给定一个区间,求区间内的美丽数字个数。 分析:首先,可以把限制条件转化为之前所有位数的 最大公倍数 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 ] 定义为 饱和 然后发现MLE了,原因在于数组开太大 ,而1~9的公倍数为2520,[ sum ]的最大值就可以看成 2520,大的就用取模(有相关数论),所以这个只要开2520。并且,[ lca ]位置很多都没用上,1~9的公倍数就那么多少个,所以就要用到 离散化 ,建立一个 Hash数组 ,在操作dp这个数组的时候,把 [ lca ]位置的值用Hash数组转化为 cnt 就行了,这个数字大概就50个。 还可以看到Lcm(a,b)=a/gcd(a,b)*b #include<cstdio> #include<cstring> #include<cmath>

HDU2089 不要62 数位dp

匿名 (未验证) 提交于 2019-12-02 23:49:02
网址: https://vjudge.net/problem/HDU-2089 题意: 从给定的范围中求出有几个数的序列中不包括$‘4’$和$‘62’$。 题解: 数位dp板子题,看注释。 AC代码: #include <bits/stdc++.h> using namespace std; int dp[20][2];//第i是j的情况 int a[20]; int dfs(int pos,int pre,bool lim,bool tag)//这一位的数字,上一位数字,上限,6的标记 { if(pos==-1)//已经到尽头了 return 1; if(!lim&&dp[pos][tag]!=-1)//记忆化搜索,一定要判断是否有边界,有边界时计算不存在情况。 return dp[pos][tag]; int res=lim?a[pos]:9;//设置边界 int ans=0; for(int i=0;i<=res;++i) { if(i==4||(i==2&&pre==6)) continue; ans+=dfs(pos-1,i,lim&&i==a[pos],i==6);//有边界且i到达了边界,下一位也才有边界 } if(!lim) dp[pos][tag]=ans;//记录 return ans; } int solve(int num) { memset(a,0

解题报告 『不要62(数位动规)』

匿名 (未验证) 提交于 2019-12-02 23:40:02
原题地址 又学会了骂人的新词语。 代码实现如下: #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (register int i = (a); i <= (b); i++) const int maxn = 10; int l, r, ans, len; int num[maxn], dp[maxn][maxn]; void origin() {memset(dp, -1, sizeof(dp));} int dfs(int pos, int limit, int lead, int is_six) { int ans = 0; if (!pos) return 1; if (!limit && !lead && dp[pos][is_six] != -1) return dp[pos][is_six]; int res = limit ? num[pos] : 9; rep(i, 0, res) { if (i == 4 || (is_six && i == 2)) continue; if (!i && lead) ans += dfs(pos - 1, (i == res) && limit, 0, i == 6); else ans += dfs(pos - 1, (i ==