BZOJ3209: 花神的数论题

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

 
注意二进制中1的个数不会很多,所以我们可以把问题转化成求1到N中有多少数在二进制下有k个1。
发现这个条件后就是简单的数位DP了,设f[i][k]表示长度为i,含k个1且开头为0的数的个数,f[i][k]表示长度为i,含k个1且开头为1的数的个数。
最后快速幂起来得到答案。
#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
typedef long long ll;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline ll read() {
    ll x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int mod=10000007;
ll f[60][60],g[60][60];
void init() {
    f[0][0]=g[0][1]=1;
    rep(len,0,58) rep(i,0,len+1) {
        g[len+1][i+1]+=f[len][i]+g[len][i];
        f[len+1][i]+=f[len][i]+g[len][i];
    }
}
int bit[60];
ll ans[60];
ll solve(ll x) {
    int len=0,cnt=0;
    while(x) bit[len++]=x&1,x>>=1;
    rep(i,0,len-2) rep(j,0,i+1) ans[j]+=g[i][j];
    dwn(i,len-1,0) {
        if(i!=len-1&&bit[i]) rep(k,0,i+1) ans[k+cnt]+=f[i][k];
        cnt+=bit[i];
    }
}
ll pow(int a,ll n) {
    if(!n) return 1;
    ll ans=pow(a,n>>1);(ans*=ans)%=mod;
    if(n&1) (ans*=a)%=mod;
    return ans;
}
int main() {
    init();solve(read()+1);ll res=1;
    rep(i,1,59) (res*=pow(i,ans[i]))%=mod;
    printf("%lld
",res);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5043566.html