Nowcoder 模拟赛 G1T2

Captain_Paul

2018-09-09 18:57:29

Personal

题意:定义$f(x)$为$x$各个数位的乘积,对于区间$[l,r]$,求有多少个数满足$L<=f(x)<=R$ ------------ 数据范围是10^18,所以显然是一个数位dp的题目 这是第二次写数位dp 上一次用循环写的肥肠丑,这道题改用记忆化搜索 和一般的数位dp题目类似,用前缀和的思想 求出$[0,r]$中满足条件的数和$[0,l-1]$中满足条件的数,相减即可 求解时,dfs有四个参数 $pos$表示枚举到这个数的第几位 $sum$表示当前所有数位的乘积 $odk$表示这一位是否有大小限制(要控制在$[l,r]$范围内) $flag$表示这一位是否是前导零 用$map$记录答案,枚举状态记忆化搜索即可 注意特判一下$l=r=L=R=0$的情况,此时的答案需要$+1$ ``` #include<cstdio> #include<cstring> #include<map> #include<algorithm> using namespace std; typedef long long ll; ll l,r,L,R,ans,num,a[20]; map<ll,ll>mp[20]; inline void div(ll x){num=0; while (x) a[++num]=x%10,x/=10;} ll dfs(ll pos,ll sum,bool odk,bool flag) { if (!pos) { if (sum>=L&&sum<=R) return 1; return 0; } if (!odk&&flag&&mp[pos].count(sum)) return mp[pos][sum]; ll now=odk?a[pos]:9,ans=0; if (flag) { for (int i=0;i<=now;i++) ans+=dfs(pos-1,sum*i,odk&&i==now,1); } else { ans+=dfs(pos-1,1,odk&&!now,0); for (int i=1;i<=now;i++) ans+=dfs(pos-1,sum*i,odk&&i==now,1); } if (!odk&&flag) mp[pos][sum]=ans; return ans; } int main() { scanf("%lld%lld%lld%lld",&l,&r,&L,&R); if (l) { memset(a,0,sizeof(a)); div(l-1); for (int i=0;i<=18;i++) mp[i].clear(); ans-=dfs(num,1,1,0); } if (r) { memset(a,0,sizeof(a)); div(r); for (int i=0;i<=18;i++) mp[i].clear(); ans+=dfs(num,1,1,0); } printf("%lld\n",ans+((!L)&&(!l))); return 0; } ```