BZOJ 3679 數字之積(數位DP)
阿新 • • 發佈:2019-01-12
一個數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; }