BZOJ 3679: 数字之积
题意:求[L, R)区间内,各个数位之积在(0, n]的数的个数。
思路:因为L,R的范围有1e18,如果每个数位都是9,9^18是最大的乘积了吧www……是不可能开的下数组的。那怎么办,我们怎么处理乘积呢?首先说,n的范围是<=1e9,所以一旦大于1e9我们可以直接return 0. 那么问题就变成了怎么处理1e9之内的乘积。(还是很大啊qaq)。
可(大)以(佬)发(们)现(说),乘积只有2、3、5、7这四个质因子,而这四个质因子在1e9内的乘积种类数是有限的,打表发现一共有:
所以我们就只需要离散化超级无敌大乘积即可!
这里我们有两种方法:(这里我用的map映射)
-
预处理:将1e9之内的乘积打出来并且存起来。
-
动态存乘积。跑dfs的时候,如果该乘积没有出现过(mp[mul] == 0),那么我们就将它存起来。
-
说到这个mp[mul] == 0,我真的哭辽。第一种方法,预处理的时候,最开始我mp的映射是从0开始的,也就是mp[1] = 0. 但是如果乘积为0那对应的也是0www,所以就造成了WAWAWAWA
-
第二种方法,那肯定也不能从0开始,因为不存在的乘积为0的嘛
-
debug的时候还以为是预处理写错了,【其实最开始真的写错了】qaq,后来看了这个题——丑数,就是找只有因子2、3、5、7的数,然后才改对。我真的好菜。这个题又用了一下午www
-
最后对自己说一句Fighting!!!!
【还有最最开始的错误做法】我是直接记录乘积是不是满足条件的状态:sta == 0: 乘积无意义;sta == 1: 乘积为0;sta == 2: 乘积<=n;sta == 3: 乘积 > n. 但是这个显然就错了叭。因为相同的sta的可能乘积并不相同,也就是说并不具有无后效性。可能跑完这个数位就更新了dp[pos][sta],但是其他的乘积的时候可能是相同的状态,但是却直接返回了之前有的。所以错!
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 5200 + 5;
const ll pri[4] = {2, 3, 5, 7};
ll n, L, R;
int a[20];
ll dp[20][maxN];
int cnt;
map<ll, int>mp;
void pre()
{
cnt = 1;
set<ll>st;
st.insert(1);
while(!st.empty())
{
ll tmp = *st.begin();
if(tmp > 1e9)
{
//printf("%d\n", mp.size());
return;
}
mp[tmp] = cnt ++;
st.erase(tmp);
for(int i = 0; i < 4; i ++ )
st.insert(tmp * pri[i]);
}
}
ll dfs(int pos, ll mul, bool lead, bool limit)
{
if(mul > n)
return 0;
// if(mp[mul] == 0)
// mp[mul] = cnt ++;
if(pos == -1)
return mul <= n && mul > 0;
if(!limit && !lead && dp[pos][mp[mul]] != -1)
return dp[pos][mp[mul]];
int up = limit ? a[pos] : 9;
ll ans = 0;
for(int i = 0; i <= up; i ++ )
{
ll nextMul;
if(!lead)
nextMul = mul * i;
else if(i != 0)
nextMul = i;
else
nextMul = 0;
ans += dfs(pos - 1, nextMul, lead && i == 0, limit && i == a[pos]);
}
if(!limit && !lead)
dp[pos][mp[mul]] = ans;
return ans;
}
ll solve(ll x)
{
int pos = 0;
while(x)
{
a[pos ++ ] = x % 10;
x /= 10;
}
return dfs(pos - 1, 0, true, true);
}
int main()
{
pre();
// cnt = 1;
while(~scanf("%lld", &n))
{//这里的dp初始化不能放在外边,因为dp值是随着n的变化而变化的。
memset(dp, -1, sizeof(dp));//虽说这个题是单组输入吧……
scanf("%lld%lld",&L, &R);
printf("%lld\n", solve(R - 1) - solve(L - 1));
}
return 0;
}
/*
1 1 1000000000000000000
ans = 18
*/