数位dp

HDU - 4352 - XHXJ's LIS(数位DP)

我只是一个虾纸丫 提交于 2019-12-06 15:23:38
链接: https://vjudge.net/problem/HDU-4352 题意: a 到 b中一个数组成递增子序列长度等于k的数的个数 思路: 因为只有10个数,使用二进制维护一个递增序列,每次更新在注释写了。 然后正常的数位DP, Dp(i, j, k),i是位置,j是当前的递增状态,k是长度。 考虑一下前缀0,重置状态 代码: #include<bits/stdc++.h> using namespace std; typedef long long LL; const int MOD = 1e9+7; const int MAXN = 1e6+10; LL F[30][1<<10][11]; int dig[30]; LL a, b; int k; int Upd(int x, int s) { //x表示当前值,s表示递增序列 for (int i = x;i < 10;i++) { if (s & (1<<i)) return (s ^ (1 << i)) | (1 << x);//找到一个比当前值大的,换成较小的 } return s | (1 << x); } int Len(int x) { int cnt = 0; while(x) { if (x&1) cnt++; x >>= 1; } return cnt; } LL Dfs(int pos, int

windy数(数位dp)

前提是你 提交于 2019-12-06 10:53:46
https://www.luogu.com.cn/blog/virus2017/shuweidp https://www.luogu.com.cn/problem/P2657 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define ll long long const int maxn = 200100; ll dp[20][10]; //dp[i][j]为当前在 i 位,前一位的数是 j 时的方案数。 ll num[20]; ll dfs(int n,bool lead,bool limit,int pre) //当前位 前导0标记 位限制 前一位数 { if(n<0) return 1; if(!limit && !lead && dp[n][pre]!=-1) return dp[n][pre]; ll ans=0; int up=limit?num[n]:9; for(int i=0;i<=up;i++) { if(!lead &&abs(pre-i)<2) continue; //无前导0,并且相邻两数小于2 直接遍历下一个 ans+=dfs(n-1,lead&&i==0,limit&&i==up,i); /

HDU3652 B-number 题解 数位DP

南笙酒味 提交于 2019-12-06 08:36:41
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3652 题目大意: 求区间 \([1, n]\) 范围内包含连续的数位“13”并且能被13整数的数的数量。 解题思路: 使用 数位DP 进行求解。 开一个状态 \(f[pos][pre][num][flag]\) ,用于表示: 当前所处的数位为第 pos 位; 之前所有的数位模13的余数为 pre ; 当前数位的前一位(即第 pos+1 位)为 num ; flag 用于表示当前数位的前面那些位中是否有连续的两位为‘13’( flag==true 表示有过, flag==false 表示还没有过) 时的方案总数。 函数 dfs(int pos, int pre, int num, int flag, bool limit) 用于求解答案,其中: pos 、 pre 、 num 、 flag 的含义同上; limit 用于表示当前是否处于限制状态。 实现代码如下: #include <bits/stdc++.h> using namespace std; int f[33][13][10][2], a[33]; void init() { memset(f, -1, sizeof(f)); } int dfs(int pos, int pre, int num, int flag,

POJ3252 Round Numbers 题解 数位DP

半腔热情 提交于 2019-12-06 08:29:52
题目大意: 求区间 \([x,y]\) 范围内有多少数的二进制表示中的‘0’的个数 \(\ge\) ‘1’的个数。 解题思路: 使用 数位DP 解决这个问题。 我们设状态 f[pos][num0][num1][all0] 表示在: 当前所在数位为 pos ; 当前选择的‘0’的个数为 num0 ; 当前选择的‘1’的个数为 num1 ; 到当前位位置是不是前面的数都是前导零(如果都是前导0则 all0==true ,否则 all==false )。 下的方案数。 我们开函数 dfs(int pos, int num0, int num1, bool all0, bool limit) 来解决这个问题。 其中, pos 、 num0 、 num1 、 all0 所表示的含义和上述表述一致, limit 表示当前是否处于限制条件。 实现代码如下: #include <cstdio> #include <cstring> int f[33][33][33][2], a[33]; void init() { memset(f, -1, sizeof(f)); } int dfs(int pos, int num0, int num1, bool all0, bool limit) { if (pos < 0) // 遍历完所有数位 return num0 >= num1 ? 1 : 0;

HDU4734 F(x) 题解 数位DP

。_饼干妹妹 提交于 2019-12-06 08:27:58
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4734 题目大意: 对于一个 \(n\) 位十进制数 \(x\) ( \(A_nA_{n-1}A_{n-2} \cdots A_2A_1\) ),我们定义 \[F(x)=A_n \times 2^{n-1} + A_{n-1} \times 2^{n-2} + \cdots + A_2 \times 2 + A_1 \times 1\] 现在给你两个数 \(A\) 和 \(B\) ,请计算出区间 \([0,B]\) 范围内有多少 \(\le F(A)\) 的数。( \(0 \le A,B \lt 10^9\) ) 解题思路: 首先 \(A \le 10^9\) 所以 \(F(A)\) 的最大值为 \[F(999999999) = 9 \times (2^8+2^7+ \cdots + 2^0) = 9 \times (2^9-1) = 4599\] 所以对于任意一个 \(\lt 10^9\) 的 \(x\) 来说, \(F(x) \le 4599\) 。 我们可以用 数位DP 来解决这个问题。 我们设状态 \(f[pos][num]\) 来表示当前数位在 \(pos\) 且剩余值不超过 \(num\) 的方案数。 然后我们开函数 dfs(int pos, int num, bool

HDU3555 Bomb 题解 数位DP

强颜欢笑 提交于 2019-12-06 05:47:25
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意:求 \([1,n]\) 范围内有多少数包含“49”。 解题思路: 这个问题我们可以分两种解法来考虑:第一种是求不包含“49”的数的数量,用后减一下;另一种就是直接求包含“49”的数的数量。 解法1:求多少数不包含“49” 这种方法我们先通过数位DP求出 \([0,n]\) 区间范围内有多少数不包含“49”(假设数量为 \(x\) ),然后可以得到答案为 \(n+1-x\) 。 我们可以设计一个函数 dfs(int pos, int stat, bool limit) 来返回区间 \([0,n]\) 范围内有多少数不包含“49”,其中: pos 表示当前所处数位; stat 表示前一位的数是不是‘4’(如果前一位是‘4’当前位是‘9’则凑成“49”); limit 用于标记当前是否受限制(true:受限制;false:不受限制)。 实现代码如下: #include <bits/stdc++.h> using namespace std; long long f[66][2], n; int T, a[66]; void init() { memset(f, -1, sizeof(f)); } long long dfs(int pos, int stat, bool

数位dp(Balanced Numbers )

做~自己de王妃 提交于 2019-12-04 20:19:13
题意:一个数,如果满足奇数的数字出现偶数次,偶数的数字出现奇数次, 就是符合的数,注比如:12313 就满足,因为1 3出现了偶数次。2出现了奇数次 思路,对于这道题,就是状态压缩加dp; 对于一个数字来说,0~9每一位,都只有3种状态量,要么从未出现,要么出现次数为奇 要么为偶,所以可以用3进制来表示,通过3进制表示出其状态,然后到pos-1的时候 判断0~9的各个状态是否符合条件即可 1 #include<cstdio> 2 #include<string.h> 3 using namespace std; 4 typedef long long ll; 5 ll dp[20][60000]; 6 ll a[20]; 7 int check(int s) 8 { 9 for(int i=0;i<=9;++i){ 10 int k=s%3; 11 s/=3; 12 if(k==0) continue; 13 else if((i&1)&&k==1) return 0; //若为奇数,如果出现次数也为奇,则退出; 14 else if(!(i&1)&&k==2) return 0; //若为偶数,。。。。。。。。。。。。。。 15 } 16 return 1; 17 } 18 //三进制代码; 19 int change(int d,int s) 20 { 21 int temp

HDOJ2089 不要62 --- 数位dp

拜拜、爱过 提交于 2019-12-04 13:51:37
题意:给定区间[n,m],1<=n<m<1,000,000 问有多少个数不含4,且不含62 题解:数位dp的典型题,参考 https://blog.csdn.net/jk211766/article/details/81474632 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,m; int d[10][3]; int number[10]; // pos: 当前位置 // sta: 前1位是否是6 int dp(int pos,int sta,bool limit) { if(pos == -1) return 1; if(d[pos][sta] != -1 && !limit) return d[pos][sta]; int up = (limit == true ? number[pos] : 9); int tot = 0; for(int i = 0;i <= up;i++) { if(i == 4) continue; if(sta == 1 && i == 2) continue; tot += dp(pos-1,i==6,limit&&i==number[pos]); } if(!limit) d[pos][sta] = tot; return

CF55D Beautiful numbers(数位dp)

余生颓废 提交于 2019-12-04 06:55:42
传送门 被几个数整除,等价于被他们的lcm整除。 于是想到设dp[i][j][k]表示到第i位,当前的数为j,lcm为k的数的个数。 这样空间肯定无法接受。考虑优化。 我们发现1,2,3,4,5,6,7,8,9的lcm不过是2520,我们每次把j%一下2520不会影响答案。 然后对于k这一维,直接存下当前的lcm也是不行的。 我们发现在真正由1,2,3,4,5,6,7,8,9构成的lcm(也就是2520的约数)其实很少,只有48个。 于是我们开个数组映射一下即可。 然后小心爆int。 #include<bits/stdc++.h> #define LL long long #define INF 2100000000 #define mod 2520 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; } void print(LL x) { if(x<0)x=-x,putchar('-'); if(x>9)print(x/10); putchar(x%10+'0'); } LL T; LL

「NOIP模拟赛」数位和乘积(dp,高精)

白昼怎懂夜的黑 提交于 2019-12-04 05:30:33
统计方案数,要么组合数,要么递推(dp)了。 这是有模拟赛历史以来爆炸最狠的一次 T1写了正解,也想到开long long,但是开错了地方然后数组开大了结果100->0 T3看错题本来简单模拟又给我搞成0分 T5差分约束本来很简单但是又被我胡搞炸掉了..... 本题T4,难到爆炸的T2把我困住了..... 先讲讲考试看道题的想法: 思考了一会吗,推出几个结论,然后准备写了,感觉可以短时间A掉,结果被T2困住,一小时只优化掉了一个没啥用的n..(n^5logn的复杂度用爱过题) 然后现在来讲讲正解(也是时候背高精板子了) 首先,很容易想到,对于有0出现的数字,其结果一定是0,从而得出一个结论,对于0的结果就是10^n-9^n(不要让我证明) 然后进一步想到,对于给定k进行质因数分解,然后组合&&排列算方案数(我就是这里走了与dp不同的路) 但是发现这样并不好处理,进一步发现,如果k的质因数有大于10的,那么直接输出0(因为一个数位没法装下两个数) 然后,我在这里就暴毙了。 接下来就是正解了。 有了以上结论,我们能非常不容易地想到dp方程式: $f[i][j][k][m][l]$表示在前i位中,2,3,5,7分别用了几次; 这个转移是真的令人折服。 for (int i = 1; i <= n;i++) { for (int j = a2; j >= 0;j--) { for (int