数位dp
acwing 1081.度的数量
https://www.acwing.com/problem/content/1083/

题意:找出区间$(X, Y) $中,用$B$进制表示并且只有$K$个1的数有多少。
类似前缀和的思想,可以先求$0$到$Y$中满足条件的数的个数,减去 $0$到$X$中满足条件的数的个数。
将数字标号,从最高位开始。对每一位进行分类讨论, 例如对于第一位数字$a_{n-1}$, 左子树代表从$0$到$a_{n-1}-1$中选取一个数字填在第一位,右子树代表该位填的数字就是$a_{n-1}$。
由于本题的数字只能填$0$或$1$, 所以左子树的情况就是$0$,右子树的情况就是填$1$。分析可知,左子树的情况实际就是组合数,所以我们要预处理组合数。
例如对于第一位的左子树,因为第一位填$0$,所以在后面的$n-1$位数中需要填入$K$个1,即$C_{n-1}^{K}$。
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int N = 35; 7 int K, B; 8 int c[N][N]; 9 10 int dp(int n) 11 { 12 if(!n)return 0; 13 14 vector<int> nums; 15 while(n)nums.push_back(n % B), n /= B; 16 17 int last = 0;//记录当前所需要的前置信息,这里是前面1的个数 18 int res = 0; 19 for(int i = nums.size() - 1 ; i >= 0 ; i --) 20 { 21 int x = nums[i]; 22 if(x)//左子树 23 { 24 res += c[i][K - last];//当前位填0 25 if(x > 1)//如果当前位能填1,那么右子树就不存在了 26 { 27 if(K - last - 1 >= 0)res += c[i][K - last - 1]; 28 break; 29 } 30 else//当前位填1 31 { 32 last ++; 33 if(last > K)break; 34 } 35 } 36 if(!i && last == K)res ++; 37 } 38 39 return res; 40 } 41 42 void init() 43 { 44 for(int i = 0 ; i < N ; i ++) 45 for(int j = 0 ; j <= i ; j ++) 46 if(!j)c[i][j] = 1; 47 else c[i][j] = c[i - 1][j] + c[i - 1][j - 1]; 48 } 49 50 int main(){ 51 int l, r; 52 init(); 53 cin >> l >> r >> K >> B; 54 55 cout << dp(r) - dp(l - 1) << endl; 56 return 0; 57 }
acwing 1082.数字游戏
https://www.acwing.com/problem/content/1084/
同上题分析,只是预处理的东西不同,该题需要预处理上升数的个数。

1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <vector> 5 6 using namespace std; 7 8 const int N = 11; 9 10 int f[N][10]; 11 12 void init() 13 { 14 for(int i = 0 ; i <= 9 ; i ++)f[1][i] = 1; 15 for(int i = 2 ; i < N ; i ++) 16 for(int j = 0 ; j <= 9 ; j ++) 17 for(int k = j ; k <= 9 ; k ++) 18 f[i][j] += f[i - 1][k]; 19 } 20 21 int dp(int n) 22 { 23 if(!n)return 1; 24 25 vector<int> nums; 26 while(n)nums.push_back(n % 10), n /= 10; 27 28 int res = 0; 29 int last = 0; 30 for(int i = nums.size() - 1; i >= 0 ; i --) 31 { 32 int x = nums[i]; 33 for(int j = last ; j < x ; j ++) 34 res += f[i + 1][j];//数组存储数字是从0开始,我们算位数是从1开始,需要加上偏移量 35 36 if(x < last)break; 37 38 last = x; 39 40 if(!i)res ++; 41 } 42 return res; 43 } 44 45 int main(){ 46 int l, r; 47 init(); 48 while(cin >> l >> r)cout << dp(r) - dp(l - 1) << endl; 49 return 0; 50 }
acwing 1083.Windy数
https://www.acwing.com/problem/content/1085/
同第一题分析,因为不能有前导零。所以有些许不同。

第一分支的第一个数字不能为$0$。左子树的和依旧是简单的$dp$。需要注意的是需要在最后加上所有有前导零的数字情况。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <vector> 5 6 using namespace std; 7 8 const int N = 35; 9 int f[N][10]; 10 11 void init() 12 { 13 for(int i = 0 ; i <= 9 ; i ++)f[1][i] = 1; 14 for(int i = 2 ; i < N ; i ++) 15 for(int j = 0 ; j <= 9 ; j ++) 16 for(int k = 0 ; k <= 9 ; k ++) 17 if(abs(j - k) >= 2) 18 f[i][j] += f[i - 1][k]; 19 } 20 21 int dp(int n) 22 { 23 if(!n)return 0; 24 25 vector<int> nums; 26 while(n)nums.push_back(n % 10), n /= 10; 27 28 int res = 0; 29 int last = -2; 30 for(int i = nums.size() - 1 ; i >= 0 ; i --) 31 { 32 int x = nums[i]; 33 for(int j = i == nums.size() - 1 ; j < x ; j ++) 34 if(abs(j - last) >= 2) 35 res += f[i + 1][j]; 36 37 if(abs(x - last) < 2)break; 38 39 last = x; 40 41 if(!i)res ++; 42 } 43 44 for(int i = 1 ; i < nums.size() ; i ++)//将位数是1, 2,3..., nums.size() - 1的windy数加上 45 for(int j = 1 ; j <= 9 ; j ++) 46 res += f[i][j]; 47 48 return res; 49 } 50 51 52 int main(){ 53 int l, r; 54 init(); 55 cin >> l >> r; 56 cout << dp(r) - dp(l - 1) << endl; 57 return 0; 58 }
acwing 1084.数字游戏②
https://www.acwing.com/problem/content/1086/

1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <vector> 5 6 using namespace std; 7 8 const int N = 35; 9 int P; 10 int f[N][10][110]; 11 12 int mod(int x, int y)//将取模都转为正数 13 { 14 return (x % y + y) % y; 15 } 16 17 void init() 18 { 19 memset(f, 0, sizeof f);//多组输入,因为P不一样所以每次都要重做一次 20 for(int i = 0 ; i <= 9 ; i ++)f[1][i][i % P] = 1; 21 for(int i = 2 ; i < N ; i ++) 22 for(int j = 0 ; j <= 9 ; j ++) 23 for(int k = 0 ; k < P ; k ++) 24 for(int x = 0 ; x <= 9 ; x ++) 25 f[i][j][k] += f[i - 1][x][mod(k - j, P)]; 26 } 27 28 29 int dp(int n) 30 { 31 if(!n)return 1; 32 33 vector<int> nums; 34 while(n)nums.push_back(n % 10), n /= 10; 35 36 int res = 0; 37 int last = 0; 38 for(int i = nums.size() - 1 ; i >= 0 ; i --) 39 { 40 int x = nums[i]; 41 for(int j = 0 ; j < x ; j ++) 42 res += f[i + 1][j][mod(-last, P)]; 43 44 last += x; 45 46 if(!i && last % P == 0)res ++; 47 } 48 return res; 49 } 50 51 int main(){ 52 int l, r; 53 while(cin >> l >> r >> P) 54 { 55 init(); 56 cout << dp(r) - dp(l - 1) << endl; 57 } 58 return 0; 59 }
acwing 1085.不要62
https://www.acwing.com/problem/content/1087/
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <vector> 5 6 using namespace std; 7 8 const int N = 11; 9 int f[N][10]; 10 11 void init() 12 { 13 for(int i = 0 ; i <= 9 ; i ++) 14 if(i != 4)f[1][i] = 1; 15 for(int i = 1 ; i < N ; i ++) 16 for(int j = 0 ; j <= 9 ; j ++) 17 for(int k = 0 ; k <= 9 ; k ++) 18 { 19 if(j == 4 || k == 4 || j == 6 && k == 2)continue; 20 f[i][j] += f[i - 1][k]; 21 } 22 } 23 24 25 int dp(int n) 26 { 27 if(!n)return 1; 28 29 vector<int> nums; 30 while(n)nums.push_back(n % 10), n /= 10; 31 32 int res = 0; 33 int last = 0; 34 for(int i = nums.size() - 1 ; i >= 0 ; i --) 35 { 36 int x = nums[i]; 37 for(int j = 0 ; j < x ; j ++) 38 { 39 if(j == 4 || last == 6 && j == 2)continue; 40 res += f[i + 1][j]; 41 } 42 43 if(x == 4 || last == 6 && x == 2)break; 44 45 last = x; 46 47 if(!i)res ++; 48 } 49 return res; 50 } 51 52 int main(){ 53 int l, r; 54 init(); 55 while(cin >> l >> r, r || l) 56 { 57 cout << dp(r) - dp(l - 1) << endl; 58 } 59 return 0; 60 }

浙公网安备 33010602011771号