zoukankan      html  css  js  c++  java
  • 「Luogu4317」花神的数论题

    「Luogu4317」花神的数论题

    思维僵化不会写题了QAQ


    problem

    题目背景

    众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。
    题目描述

    话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。 花神的题目是这样的:设 (sum(i)) 表示 (i) 的二进制表示中 (1) 的个数。给出一个正整数 (N) ,花神要问你 (prod_{i=1}^Nsum(i)) ,也就是(sum(1)∼sum(N))的乘积。

    输入输出格式

    输入格式:

    一个正整数 (N)

    输出格式:

    一个数,答案模 (10000007) 的值。

    输入输出样例

    输入样例#1:

    3
    

    输出样例#1:

    2
    

    说明

    对于 (100%) 的数据,(N≤10^{15})

    Solution I

    位数计算显然可以用数位DP来搞

    有一位神仙曾经说过

    当你不会写DP的时候,你就可以先写一个暴搜,然后再改成记搜 ——神仙

    一语点醒梦中人,如雷贯耳(雾)

    于是我们考虑按位数从高到低暴搜,并且记录(1)的个数。
    特别地,我们令(sum(0)=1)

    然后加上记忆化就完事了

    Code

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <iostream>
    #define maxb 69
    using namespace std;
    typedef long long ll;
    
    const ll mod=10000007;
    ll n;
    ll lim[maxb],len;
    ll dp[maxb][maxb];
    
    ll Search(ll pos,ll cnt,bool limit)
    {
        if(!pos)return max(cnt,1LL);
        if(~dp[pos][cnt] && !limit)return dp[pos][cnt];
        ll re=1;
        for(register ll i=0;i<=(limit?lim[pos]:1);++i)
            re=re*Search(pos-1,cnt+i,limit&&(i==lim[pos]))%mod;
        if(!limit)dp[pos][cnt]=re;
        return re;
    }
    
    int main()
    {
        scanf("%lld",&n);
        memset(dp,-1,sizeof(dp));
        while(n)
        {
            lim[++len]=n&1;
            n>>=1;
        }
        printf("%lld",Search(len,0,1));
        return 0;
    }
    
    

    Solution II

    我们可以换个思路

    我们可以通过数位DP求出在([1,N])的所有数中,二进制下(1)的个数为(1,2,3,dots,len)的数的个数

    然后使用快速幂即可求出答案

    于是(dp[i][j])表示枚举到第(i)位,前(i)位已经有(j)(1)的答案个数

    然后可以愉快地套模板辣

    Code

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <iostream>
    #define maxb 69
    using namespace std;
    typedef long long ll;
    
    const ll mod=10000007;
    ll n;
    ll lim[maxb],len;
    ll dp[maxb][maxb],cnts[maxb],ans=1;
    
    ll Search(ll cur,ll pos,ll cnt,bool limit)
    {
    	if(!pos)return cnt==cur;
    	if(~dp[pos][cnt] && !limit)return dp[pos][cnt];
    	ll re=0;
    	for(register ll i=0;i<=(limit?lim[pos]:1);++i)
    		re+=Search(cur,pos-1,cnt+i,limit&&(i==lim[pos]));
    	if(!limit)dp[pos][cnt]=re;
    	return re;
    }
    
    ll fastpow(ll a,ll b)
    {
    	ll re=1,base=a;
    	while(b)
    	{
    		if(b&1)re=re*base%mod;
    		base=base*base%mod;
    		b>>=1;
    	}
    	return re;
    }
    
    int main()
    {
    	scanf("%lld",&n);
    	while(n)
    	{
    		lim[++len]=n&1;
    		n>>=1;
    	}
    	for(register ll i=1;i<=len;++i)
    	{
    		memset(dp,-1,sizeof(dp));
    		cnts[i]=Search(i,len,0,1);
    //		cerr<<cnts[i]<<" ";
    	}
    	for(register int i=1;i<=len;++i)
    		ans=ans*fastpow(i,cnts[i])%mod;
    	printf("%lld",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    我们需要什么,技术还是idea
    爱上一个人,忘记一个人
    我的大学
    早上8点,轻轨抛锚时... ...
    我的秋天
    通过注册表以及文件操作的方式获取当前正在实际使用的物理网卡MAC地址
    【分享】全局字符串转换为局部变量存储防止软件被静态分析暴露敏感字符串
    正确获取硬盘序列号源码
    【转】Xvid参数详解
    VerifyFile验证文件签名
  • 原文地址:https://www.cnblogs.com/lizbaka/p/10402398.html
Copyright © 2011-2022 走看看