zoukankan      html  css  js  c++  java
  • luogu_4317: 花神的数论题

    花神的数论题

    题意描述:
    • (sum(i))表示(i)的二进制数中(1)的个数。
    • 给定一个整数(N),求(prod_{i=1}^Nsum(i))
    输入描述:
    • 输入包含一个正整数(N(Nleq10^{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(Nleq 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
    ", 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;
    }
    
    
  • 相关阅读:
    awk二十问-【AWK学习之旅】
    awk十三问-【AWK学习之旅】
    awk分割列-【AWK学习之旅】
    securecrt重建
    SQuirrel-GUI工具安装手册-基于phoenix驱动
    phoenix部署手册-基于hbase
    MySQL5.6一键部署
    CSV文件导入导出MySQL
    Percona-Server-5.7.16 启动错误
    Linux下安装php加速软件Xcache
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/11725517.html
Copyright © 2011-2022 走看看