[BZOJ]3679 数字之积

题意

一个数x各个数位上的数之积记为f(x) <不含前导零>
求[L,R)中满足\(0<f(x)<=n\)的数的个数

题解

(第一次独立做出来的数位dp)
虽然也是很简单,熟悉数位dp的套路就行了。
数位dp是不是只要写个爆搜然后加个记忆化就行了啊?
先写了的爆搜, \(dfs(i, limit, x, lead)\)表示当前搜到第i位,是否受顶端限制为limit,前面的乘积为x,当前是否为前导0为lead。

然后加记忆化因为这个dp是求\(dfs\)函数的值,只需要保存几个自变量对应的因变量值就行了,但是x范围很大。
但是其实x能取的值很少,因为x包含的质因子只有10以内的,用map存状态就行了(其实是运气好,并没有分析x的范围)。

#include <bits/stdc++.h>
#define int long long
#define Mid ((l + r) >> 1)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
using namespace std;
int read(){
	char c; int num, f = 1;
	while(c = getchar(),!isdigit(c)) if(c == '-') f = -1; num = c - '0';
	while(c = getchar(), isdigit(c)) num = num * 10 + c - '0';
	return f * num;
}
map<int, int>f[109][2][2];
int n, len, a[109];
char s[109];
int dfs(int i, int limit, int x, int lead) {
	if(x > n || x <= 0) return 0;
	if(f[i][limit][lead].count(x)) return f[i][limit][lead][x];
	if(i == len + 1) return x <= n && lead == 0;
	int up = (limit ? a[i] : 9), ans = 0;
	if(lead) ans += dfs(i + 1, 0, x, 1);
	for(int p = 1; p <= up; p++) {
		ans += dfs(i + 1, limit && p == up, x * p, 0);
	}
	f[i][limit][lead][x] = ans;
	return ans;
}
int query(int x) {
	len = 0;
	for(int i = 0; i < 109; i++)
		for(int j = 0; j <= 1; j++) 
			for(int k = 0; k <= 1; k++)	
				f[i][j][k].clear();
	while(x) {
		a[++len] = x % 10;
		x /= 10;
	}
	reverse(a + 1, a + 1 + len);
	return dfs(1, 1, 1, 1);
}
signed main()
{
	int ans = 0, l, r;
	n = read();
	l = read(); r = read();
	printf("%lld\n", query(r - 1) - query(l - 1));
	return 0;
}
/*
dfs(i, x, z)表示前i位,是否顶到上限,积为z的方案数。
*/
posted @ 2021-02-09 12:47  _onglu  阅读(92)  评论(0编辑  收藏  举报