• [bzoj3209][花神的数论题] (数位dp+费马小定理)


    Description

    背景
    众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。
    描述
    话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。
    花神的题目是这样的
    设 sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你
    派(Sum(i)),也就是 sum(1)—sum(N) 的乘积。

    Input

    一个正整数 N。

    Output

    一个数,答案模 10000007 的值。

    Sample Input

    样例输入一

    3


    Sample Output

    样例输出一

    2

    HINT



    对于样例一,1*1*2=2;


    数据范围与约定


    对于 100% 的数据,N≤10^15

    Solution

    简单的数位dp,是论文的简化

    一样,预处理出f[i][j]代表i长度的二进制数下有j个1的数的数量

    求解具体数据先枚举1的数量,转移一下就行,之后用快速幂连乘

    难点:费马小定理的应用,对于极大的f[i][j],由于它是指数,无法直接和md取模,

    根据 a^phi(p)≡1(mod p),得出把f[i][j]和phi(10000007)=9988440取模就行了

    #include<iostream>
    #define ur 9988440
    #define md 10000007
    #define LL long long
    LL n,ans=1LL,f[53][53],tim[53];
    void init() {
        f[0][0]=1LL;
        for(int i=1; i<=50; i++) {
            f[i][0]=f[i-1][0];
            for(int j=1; j<=i; j++)
                f[i][j]=(f[i-1][j-1]+f[i-1][j])%ur; } }
    LL Q_pow(LL x,LL p) {
        LL res=1LL;
        for(; p; p>>=1LL) {
            if(p&1LL)
                res=(res*x)%md;
            x=(x*x)%md; }
        return res; }
    void calc() {
        int cnt=0;
        for(int i=50; ~i; i--) {
            if(n&(1LL<<i)) {
                for(int j=cnt; j<=50; j++)
                    tim[j]=(tim[j]+f[i][j-cnt])%ur;
                cnt++; } } }
    int main() {
        init();
        std::ios::sync_with_stdio(false);
        std::cin>>n; n++; calc();
        for(int i=1; i<=50; i++)
            ans=(ans*Q_pow(i,tim[i]))%md;
        std::cout<<ans<<std::endl;
        return 0; }
  • 相关阅读:
    Integer类的parseInt和valueOf的区别
    华为实习小结
    程序员浪费生命的几种方式
    移动前端中viewport(视口) 转
    Console API 与命令行
    Ajax
    浏览器缓存机制
    mysql之各种命令总结
    jquery file upload 文件上传插件
    文件上传插件uploadify详解
  • 原文地址:https://www.cnblogs.com/keshuqi/p/6282412.html
Copyright © 2020-2023  润新知