BZOJ-3209 (数论)

本文介绍了一道名为“花神的数论题”的算法题目,该题要求计算从1到N范围内所有数的二进制表示中1的个数的乘积,并给出了一种高效的算法实现方案。通过将问题分解为多个子区间,利用组合数学的方法求解。

题目

3209: 花神的数论题
Time Limit: 10 Sec Memory Limit: 128 MB

Problem Dexcription

设 sum(i) 表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘积。

Input

一个正整数 N。
N≤10^15

Output

一个数,答案模 10000007 的值。

Samples

InputOutput
5712560979
32
42
824
948
10000000000000001030503

分析


  • 可以把 n 看成二进制,分段来讨论。
  • 举个例子模拟一遍即可意会:

n=100100(二进制下)
000001~011111 贡献为1C152C253C354C455C15
100000 贡献为 1
100001~100011 贡献为 1C122C22
100100 贡献为 2
*有那么一点数位 dp 用到的思想,对于 ([1~2^n) 这个区间里所有数对答案的贡献我们可以用上面的组合数方法求出来(分类 一个1,两个1,三个1…),然后就可以把原来的 n 分成许多个这样连续的“满二次幂”区间(临时造词),注意一下底数要加上前面已经固定好的 1 的数量(我的程序中体现为 cnt)。
* 可以再看看程序理解吧。

程序

#include <cstdio>
#define Ha 10000007
long long i,j,n,p,q,cnt,ret,ans=1ll,C[60][60];

long long ksm(long long x,long long y){
    for (ret=1; y; y>>=1,x=(x*x)%Ha)
        if (y&1) ret=(ret*x)%Ha;
    return ret;
}

int main(){
    for (C[0][0]=1,i=1; i<=55; i++)
        for (j=0; j<=55; j++)
            C[i][j]=C[i-1][j-1]+C[i-1][j];

    scanf("%lld",&n);
    for (cnt=0,p=53,q=1ll<<p; q; q>>=1,p--) if (n&q){
        for (i=1; i<=p; i++)
            ans=(ans*ksm(cnt+i,C[p][i]))%Ha;
        ans=(ans*(++cnt))%Ha;
    }

    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值