• 花神的数论题(这题...哎。数位dp咋就这么 not naive 呢)


    题意简介

    没什么好说,就是让你求出 1 ~ n 之间每个数转化为二进制后 '1' 的个数,然后乘起来输出积

    题目分析

    emmmm.... 两种解法(同是 $O(log^2 N)$ 的算法,组合数效率完爆 数位dp,当然是我自己的数位dp)。

    1. 组合数直接艹。(数据范围8e9 能过,其实这个东西你搞一搞 __int128 什么的再找个大质数也许也能过去啦)(好吧这个直接暴力预处理组合数的数组就好了)
    2. 老老实实数位dp。(可以AC)

    于是翻车了...这么久

    算法实现

    1. 组合数 

    组合数非常好做,只需要想想第 i 位为 1 的位上后面那一堆数字里面挑出 j 个数的方案就好了,注意加上 cnt ,然后最后快速幂累加一下答案,分分钟搞定

    然后这里用了逆元处理组合数要预处理的阶乘,如果你还不会线性求逆元的话可以点这里

    当然这里用逆元...就是作死...我们完全可以预处理出 50 * 50 的杨辉三角数组,存好组合数直接用

    2.数位dp

    ...不就是枚举 1 的个数然后记忆化深搜么?hem...听起来完全没有组合数高级

    代码实现

    1.组合数

     1 //by Judge
     2 #include<iostream>
     3 #include<cstdio>
     4 #define ll long long
     5 using namespace std;
     6 const int M=55;
     7 const ll mod=1e7+7;
     8 ll n,len,cnt,ans=1;
     9 ll C[M][M],d[M],num[M];
    10 inline void prep(){  //预处理组合数模板? 
    11     for(int i=0;i<=50;++i) C[i][0]=1;
    12     for(int i=1,j;i<=50;++i) for(j=1;j<=50;++j)
    13         C[i][j]=C[i-1][j]+C[i-1][j-1];
    14 }
    15 inline ll quick_pow(ll x,ll p,ll ans=1){  //快速幂模板?
    16     while(p){
    17         if(p&1) ans=ans*x%mod;
    18         x=x*x%mod, p>>=1;
    19     } return ans;
    20 }
    21 signed main(){
    22     cin>>n,prep();
    23     while(n) d[++len]=n&1,n>>=1;//转化二进制
    24     for(ll i=len,j;i;--i) if(d[i]){
    25         for(j=1;j<i;++j) //组合数就是随便乱艹的算法
    26             num[cnt+j]+=C[i-1][j];
    27         ++num[++cnt];
    28     }
    29     for(ll i=1;i<=len;++i) //直接累乘就好
    30         ans=ans*quick_pow(i,num[i])%mod;
    31     cout<<ans<<endl; return 0;
    32 }

    2.数位dp

     1 //by Judge
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 #define ll long long
     6 using namespace std;
     7 const int M=55;
     8 const int mod=1e7+7;
     9 int cnt,x[M];
    10 ll n,f[M][2][M][M],num[M];
    11 inline ll quick_pow(ll x,ll p,ll ans=1){
    12     while(p) (p&1) && (ans=ans*x%mod),x=x*x%mod, p>>=1;
    13     return ans;
    14 }
    15 ll dp(int cur,int up,int tmp,int d){ //记忆化深搜,log^2 n 无压力
    16     if(!cur) return tmp==d; //特判直接返回
    17     if(~f[cur][up][tmp][d]) return f[cur][up][tmp][d]; // 已记忆,直接返回
    18     int lim=up?x[cur]:1;
    19     ll res=0;
    20     for(int i=0;i<=lim;++i) //继续深搜
    21         res+=dp(cur-1,up&&i==lim,tmp+(i==1),d);
    22     return f[cur][up][tmp][d]=res; //记忆化
    23 }
    24 ll solv(){
    25     while(n) x[++cnt]=n&1,n>>=1; //同上转化
    26     for(int i=1;i<=50;++i)  //枚举要放入的 1 的位数
    27         memset(f,-1,sizeof(f)),
    28         num[i]=dp(cnt,1,0,i);
    29     ll res=1;
    30     for(int i=1;i<=50;++i) //累乘进答案
    31         res=res*quick_pow(i,num[i])%mod;
    32     return res;
    33 }
    34 signed main(){ cin>>n,cout<<solv()<<endl; return 0; }
  • 相关阅读:
    2016-09-13面试记录
    javascript中的深度拷贝的实现过程及深拷贝的几种方法。
    javascript中的for in循环
    常见的兼容问题及其解决方法。
    一次清空所有数据方法
    数组排序
    css对齐 挖坑~
    css reset样式重置
    CSS 表单
    CSS 表格
  • 原文地址:https://www.cnblogs.com/Judge/p/9540939.html
Copyright © 2020-2023  润新知