[学习笔记+做题记录] 数位 DP

一、数位 DP

你说的对,但是数位 DP 是用于解决一种数位统计类似的问题,往往数据范围很大,比如 \(10^9, 10^{12}, 10^{18}\) 甚至更大,这种 DP 一般需要记录 当前考虑到第几位,是否贴上界 / 下界,以及一些题目里的东西,需要具体题目具体分析。

二、HDU 3652 - B-number

https://acm.hdu.edu.cn/showproblem.php?pid=3652

\(f_{i, 0/1, 0/1, j, k}\) 表示现在考虑到第 \(i\) 位,是否贴上界,是否出现连续 \(13\),以及 \(\bmod 13\) 的值和上一位的数。

然后就直接按照模拟转移即可,没什么好说的。

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
//#define int long long
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline int read () {
	int x = 0, f = 0;
	char c = getchar ();
	for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
	for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
	return !f ? x : -x;
}
int awa, n, num[15], f[15][2][2][13][10];
inline void solve (int awa) {
	n = 0;
	memset (f, 0, sizeof f);
	while (awa) {
		num[++ n] = awa % 10;
		awa /= 10;
	}
	reverse (num + 1, num + 1 + n);
	f[0][1][0][0][0] = 1;
	for (int i = 0;i < n; ++ i) {
		for (int j = 0;j < 2; ++ j) {
			for (int k = 0;k < 2; ++ k) {
				for (int mod13 = 0;mod13 < 13; ++ mod13) {
					for (int las = 0;las < 10; ++ las) {
						for (int _ = 0;_ < 10; ++ _) {
							if (j == 0) {
								auto &tmp = f[i + 1][0][k | (las == 1 && _ == 3)][(mod13 * 10 + _) % 13][_];
								tmp += f[i][j][k][mod13][las];
							}
							else {
								if (_ == num[i + 1]) {
									auto &tmp = f[i + 1][1][k | (las == 1 && _ == 3)][(mod13 * 10 + _) % 13][_];
									tmp += f[i][j][k][mod13][las];
								}
								else if (_ < num[i + 1]) {
									auto &tmp = f[i + 1][0][k | (las == 1 && _ == 3)][(mod13 * 10 + _) % 13][_];
									tmp += f[i][j][k][mod13][las];
								}
							}
						}
					}
				}
			}
		}
	}
	int ans = 0;
	for (int i = 0;i < 10; ++ i) ans += f[n][0][1][0][i];
	for (int i = 0;i < 10; ++ i) ans += f[n][1][1][0][i];
	printf ("%d\n", ans);
	return ;
}
signed main () {
	int x;
	while (cin >> x) solve (x);
	return 0;
}
// Always keep it simple and stupid

三、洛谷 P4317 花神的数论题

https://www.luogu.com.cn/problem/P4317

\(f_{i, 0/1, j}\) 表示现在考虑到(二进制下的)第 \(i\) 位,是否贴上界,有 \(j\)\(1\) 的方案数。

\(g_k = f_{n,0,k} + f_{n,1,k}\),即有 \(k\)\(1\) 的数的个数。

所以答案就是:\(\prod\limits_{k}k^{g_k}\)

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
#define int long long
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline int read () {
	int x = 0, f = 0;
	char c = getchar ();
	for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
	for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
	return !f ? x : -x;
}
const int mod = 10000007;
int f[55][2][55], num[55], n, awa;
inline int pow_mod (int a, int b, int p) {
	int res = 1;
	while (b) {
		if (b & 1) res = res * a % p;
		b >>= 1, a = a * a % p; 
	}
	return res;
}
signed main () {
	awa = read ();
	while (awa) {
		num[++ n] = awa % 2;
		awa /= 2;
	}
	reverse (num + 1, num + 1 + n);
	memset (f, 0, sizeof f);
	f[0][1][0] = 1;
	for (int i = 0;i < n; ++ i) {
		for (int j = 0;j < 2; ++ j) {
			for (int k = 0;k <= i; ++ k) {
				for (int _ = 0;_ < 2; ++ _) {
					if (!j) {
						auto &tmp = f[i + 1][0][k + _];
						tmp += f[i][j][k];
					}
					else {
						if (_ == num[i + 1]) {
							auto &tmp = f[i + 1][1][k + _];
							tmp += f[i][j][k];
						}
						else if (_ < num[i + 1]) {
							auto &tmp = f[i + 1][0][k + _];
							tmp += f[i][j][k];
						}
					}
				}
			}
		}
	}
	int ans = 1;
	for (int i = 1;i < 55; ++ i) ans = ans * pow_mod (i, f[n][0][i] + f[n][1][i], mod) % mod;
	printf ("%lld\n", ans);
	return 0;
}
// Always keep it simple and stupid
posted @ 2023-05-12 14:37  CountingGroup  阅读(13)  评论(0编辑  收藏  举报