BZOJ 3679 数字之积

3679: 数字之积

Time Limit: 10 Sec  Memory Limit: 128 MB

Description

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

Input

第一行一个数n
第二行两个数L、R

Output

一个数,即满足条件的数的个数

Sample Input

5
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);
}

 

posted @ 2018-04-03 22:43  litos  阅读(536)  评论(0编辑  收藏  举报