1. 程式人生 > >BZOJ 3679 數字之積(數位DP)

BZOJ 3679 數字之積(數位DP)

一個數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

PS:因為資料很大,所以暴力是行不通的,這裡就可以用到數位DP了,因為這個題,前置0,對乘積有影響,所以我們要宣告一個變數來判斷前置0,因為乘積比較大,所以我們可以開個map離散化,剩下的就是模板了。這裡有份很好的模板,可以參考下。

傳送門。其他細節看程式碼。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn=1e5+10;
const int mod=10007;
const int inf=1e8;
#define me(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
typedef long long ll;
using namespace std;
ll l,r,dp[20][maxn];
int n,d[20],cnt;
map<ll,int>q;
ll dfs(int pos,ll sum,int lead,int lit)//lead為0時有前導0,反之則無
{
    if(sum>n)
        return 0;
    if(!q[sum])//離散化
        q[sum]=cnt++;
    if(pos==0)
        return sum>0&&sum<=n;
    if(lead&&!lit&&dp[pos][q[sum]]!=-1)
        return dp[pos][q[sum]];
    ll ans=0;
    int End=lit?d[pos]:9;
    for(int i=0; i<=End; i++)
    {
        if(lead)//無前導0
            ans+=dfs(pos-1,sum*i,1,lit&&i==End);
        else
        {
            if(i==0)
                ans+=dfs(pos-1,sum*i,0,lit&&i==End);//有前置0,當前也為0
            else
                ans+=dfs(pos-1,i,1,lit&&i==End);//有前置0,但是當前位不為0.
        }
    }
    if(!lit&&lead)
        dp[pos][q[sum]]=ans;
    return ans;
}
ll solve(ll n)
{
    int len=0;
    while(n)
    {
        d[++len]=n%10;
        n/=10;
    }
    return dfs(len,0,0,1);
}
int main()
{
    scanf("%d",&n);
    scanf("%lld%lld",&l,&r);
    q.clear(),cnt=1;
    me(dp,-1);
    printf("%lld\n",solve(r-1)-solve(l-1));
    return 0;
}