[P4317 花神的数论题]题解
P4317 花神的数论题【数位DP】
最开始其实没有什么想法
第一次遇见数位dp求相乘的题
想就按照常规做法来做,但不知道如何去处理*
于是写了一个错误的代码
//当前枚举到第id位,前面的1的个数为sum,是否达到上限limit
ll dfs(int id,int sum,int limit){
//1.出口
if(id==0) return 1ll*sum;
if(dp[id][sum][limit]!=-1) return dp[id][sum][limit];
//2.能做的事情
int bound=limit?a[id]:1;
ll res=1;
for(int i=(id==1?1:0);i<=bound;i++)
res=1ll*res*dfs(id-1,sum+(i==1),limit&&i==bound)%mod;
return dp[id][sum][limit]=res%mod;
}
ll solve(ll x){
len=0;
memset(dp,-1,sizeof(dp));
while(x){
a[++len]=x%2;
x/=2;
}
return dfs(len,0,1);
}
很明显我知道是错的
但又不太清楚是哪里错了
应该是在乘法的记忆化上面出了一些问题
提交上去也只有10分
……
[看了看题解]
……
我们不妨换一个角度来思考
它要求每一个数的
由于
所以其实
也就是
所以我们可以把每一个
这样就是一个很典型的数位dp了
但对于每一个不同的
我们都要用一次
会导致
//当前枚举到第id位,前面1的数量为sum,正在统计num的个数,是否达到上限limit
//统计cnt[sum],sum出现的次数
ll dfs(int id,int sum,int num,int limit){
//1.出口
if(id==0) return sum==num;
if(dp[id][sum][num][limit]!=-1) return dp[id][sum][num][limit];
//2.能做的事情
int bound=limit?a[id]:1;
ll res=0;
for(int i=0;i<=bound;i++)
res+=dfs(id-1,sum+(i==1),num,limit&&i==bound);
return dp[id][sum][num][limit]=res;
}
ll quickpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b/=2;
}
return res%mod;
}
int main(){
/*2023.4.15 hewanying P4317 花神的数论题 数位DP*/
scanf("%lld",&n);
memset(dp,-1,sizeof(dp));
len=0;
while(n){
a[++len]=n%2;
n/=2;
}
ans=1ll;
for(int i=1;i<=len;i++)
ans=ans*quickpow(1ll*i,dfs(len,0,i,1))%mod;
printf("%lld\n",ans);
return 0;
}
总结
对于这种要转个弯的数位dp题,我们的目的还是要把乘积转化成加法
考虑答案的构成,把相同的sum放在一块
这样就可以把题目中的
便可以用数位dp解决了
本文作者:H_W_Y
本文链接:https://www.cnblogs.com/H-W-Y/p/17320946.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步