luogu_4317: 花神的数论题

花神的数论题

题意描述:
  • \(sum(i)\)表示\(i\)的二进制数中\(1\)的个数。
  • 给定一个整数\(N\),求\(\prod_{i=1}^Nsum(i)\)
输入描述:
  • 输入包含一个正整数\(N(N\leq10^{15})\)
输出描述:
  • 一个数,答案模\(10000007\)的值。
解题思路:
  • 数位\(dp\)+快速幂。
  • \(f(i,j,k)\)表示以\(k\)开头的\(i\)位数中\(1\)的个数为\(j\)的数量。有转移方程
    • \(f(i,j,0)=f(i-1,j,0)+f(i-1,j,1)\)
    • \(f(i,j,1)=f(i-1,j-1,0)+f(i-1,j-1,1)\)
    • 这个很好理解,就是往最高位填\(0/1\)
  • \(sum(i)==x\)\(i\)\(tot\)个,那么他对答案的贡献显然就是\(x^{tot}\)
  • 所以这时候枚举\(x\)就行,显然对于\(N(N\leq 10^{15})\)而言,不会枚举超过\(60\)次。
  • 详见代码:
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 70, mod = 10000007;
ll f[maxn][maxn][2];
ll n, ans;

inline ll qmi(ll a, ll b)
{
    ll res = 1;
    while(b)
    {
        if(b & 1) res = (res * a) % mod;
        a = (a * a) % mod;
        b >>= 1ll;
    } return res % mod;
}

//x对应的二进制中有多少个1
inline ll len(ll x)
{
    ll res = 0;
    while(x)
    {
        if(x & 1ll) res++;
        x >>= 1ll;
    }
    return res;
}

int num[maxn], cnt;
inline void work(ll x)
{
    cnt = 0; ans = 1;
    while(x) //分解数位
    {
        num[++cnt] = x&1;
        x >>= 1;
    }

    //枚举m, 求出有多少i,有sum(i)=m
    for(int m = 1; m <= cnt; m++)
    {
        for(int i = 1; i < cnt; i++)
            ans = (ans * qmi(m, f[i][m][1]) % mod) % mod;
        //先枚举位数比n要小的数
        
        //开始处理位数和n相同的数字
        int k = m;
        //如果n的最高位是1的话
        //其实相当于把最高位固定了
        if(num[cnt]) k--;
        
        //从高位往低位枚举
        //之后我们让第i位小于n的第i位
        //这样可以让第i位后面的数字随便填写 
        for(int i = cnt - 1; i >= 1; i--)
        {
            for(int j = 0; j < num[i]; j++)
                ans = (ans * qmi(m, f[i][k][j]) % mod) % mod;
            if(num[i]) k--; //相当于把第i位固定 因为i有1 所以k--
            if(k < 0) break;
        }
    }
    
    //由于一直卡上界,所以其实一直没有遇到等于n的情况
    //所以最后对n暴力分解数位处理一下
    ans = ((ans % mod) * (len(n) % mod)) % mod;
    printf("%lld\n", ans);
}

inline void init()
{
    scanf("%lld", &n);
    //dp预处理
    f[1][0][0] = f[1][1][1] = 1;
    for(int i = 1; i <= 60; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            f[i+1][j+1][1] += f[i][j][0] + f[i][j][1];
            f[i+1][j][0] += f[i][j][0] + f[i][j][1];
        }
    } work(n);
}

int main()
{
    init();
    return 0;
}

posted @ 2019-10-23 11:58  zhaoxiaoyun  阅读(99)  评论(0编辑  收藏  举报