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

对于样例一,112=2;

数据范围与约定

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

Solution

简单的数位dp.

枚举1的个数, 然后dp求出数的个数.

注意指数不能取模!
由于指数意义是数的个数, 显然不会超过long long, 直接快速幂即可.

Code

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//---------------------------------------
const ll nsz=60,nmod=1e7+7;
ll n;
ll dig[nsz],pd=0;
ll dp[nsz][nsz];
ll qp(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1)res=res*a%nmod;
		a=a*a%nmod,b>>=1;
	}
	return res;
}
ll sol1(ll cnt){
	ll res=0;
	repdo(i,pd,1){
		if(dig[i])res=res+dp[i-1][cnt],--cnt;
		if(cnt<0)break;
	}
	if(cnt==0)++res;
	return res;
}
ll sol(){
	while(n)dig[++pd]=n&1,n>>=1;
	rep(i,0,pd)dp[i][0]=1;
	rep(i,1,pd)rep(j,1,i)dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
//	rep(i,0,pd){rep(j,0,pd)cout<<dp[i][j]<<' ';cout<<'\n';}
	ll ans=1;
	rep(i,1,pd){
		ans=(ans*qp(i,sol1(i)))%nmod;
	}
	return ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	cout<<sol()<<'\n';
	return 0;
}
posted @ 2019-01-07 20:02  Ubospica  阅读(184)  评论(0编辑  收藏  举报