BZOJ 3679 数字之积
3679: 数字之积
Time Limit: 10 Sec Memory Limit: 128 MBDescription
一个数x各个数位上的数之积记为f(x) <不含前导零>
求[L,R)中满足0<f(x)<=n的数的个数
Input
第一行一个数n
第二行两个数L、R
Output
一个数,即满足条件的数的个数
Sample Input
5
19 22
19 22
Sample Output
1
HINT
100% 0<L<R<10^18 , n<=10^9
分析
数位dp。真的是不会这种题啊,看着题解搞了一晚上。。。
依照题意,可以设 dp[i][j]为i位的数的乘积为j的个数。由于n<=1e9,可以得出所有乘积数都是由2、3、5、7组成的,且每位数的乘积最多有几千种,因此我们可以预处理出乘积,并排序映射成dp[i][j]中的j。首先i位数一定是由i-1位数的状态乘上1-9得到的,按照这样的转移就能得到dp数组,然后对于区间[1,R)统计答案,首先将位数比R小的数都统计了,然后按位分解R,逐位处理(重点)。假如当前为的数为x3x2x1x0,把x3拿出来,计算位数为3的乘积为n/x3的数量,这样相当于x3这一位数字乘上任何位数为3的各位数的乘积都是小于等于n的。此后同理,具体看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long f[10001];
long long d[19][10001],s[19][10001];
long long l,r,n;
int tot=0;
long long power(long long a,int t){
long long temp;
if (t==0) return 1;
if (t==1) return a;
temp=power(a,t/2);
if (t%2==0) return temp*temp;
else return temp*temp*a;
}
long long work(long long x,int n)
{
int i,ws=0,now;
long long xx=x,ans=0;
int a[20];
while(xx){
a[ws++]=xx%10;
xx/=10;
}
for (i=1;i<ws;++i) ans += s[i][upper_bound(f+1,f+tot+1,n)-f-1];
for(int i=ws;i>0;i--){
for(int k=1;k<a[i-1];++k){
if(n>=k){
if(i>1)
ans += s[i-1][upper_bound(f+1,f+tot+1,n/k)-f-1];
else ans++;
}
}
if(a[i-1]) n/=a[i-1];
else break;
}
return ans;
}
int main()
{
int i,j,k,p;
long long z,ans;
scanf("%lld%lld%lld",&n,&l,&r);
for (i=0;i<=32;++i)
for (j=0;j<=19;++j)
for (k=0;k<=16;++k)
for (p=0;p<=11;++p)
{
z=power(2,i)*power(3,j)*power(5,k)*power(7,p);
if (z<=n&&z>0)
f[++tot]=z;
}
sort(f+1,f+tot+1);
s[1][0]=0;
for (i=1;i<=9;++i) d[1][i]=1;
for (j=1;j<=tot;++j)
s[1][j]=s[1][j-1]+d[1][j];
for (i=2;i<=18;++i)
{
for (j=1;j<=upper_bound(f+1,f+tot+1,power(10,i))-f-1;++j)
for (k=1;k<=9;++k)
if (f[j]%k==0)
d[i][j]+=d[i-1][lower_bound(f+1,f+tot+1,f[j]/k)-f];
s[i][0]=0;
for (j=1;j<=tot;++j)
s[i][j]=s[i][j-1]+d[i][j];
}
ans=work(r,n)-work(l,n);
printf("%lld\n",ans);
}

浙公网安备 33010602011771号