BZOJ 3209: 花神的数论题 [数位DP]

3209: 花神的数论题

题意:求\(1到n\le 10^{15}\)二进制1的个数的乘积,取模1e7+7


二进制最多50位,我们统计每种1的个数的数的个数,快速幂再乘起来就行了
裸数位DP..\(f[i][j]\)i位数j个1的方案数..不考虑天际线就是组合数...


比较坑的地方是本题求f要取模\(phi(1e7+7)\),然后它并不是质数...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=60;
const ll P=10000007, Phi=9988440;
inline ll read(){
	char c=getchar();ll x=0,f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*f;
}

ll n, ans=1; int a[N], len;
ll Pow(ll a, ll b) { //printf("Pow %lld %lld\n",a,b);
	ll ans=1;
	for(; b; b>>=1, a=a*a%P) 
		if(b&1) ans=ans*a%P;
	return ans;
}
ll f[N][N];
ll dfs(int d, int sky, int x) {
	if(d==0) return x==0;
	if(!sky) return f[d][x];
	int lim = sky ? a[d] : 1; 
	ll now=0;
	for(int i=0; i<=lim; i++) (now += dfs(d-1, sky && i==lim, x-i))%=Phi;
	return sky ? now : f[d][x]=now;
}
int main() {
	freopen("in","r",stdin);
	n=read(); 
	while(n) a[++len]=n&1, n>>=1;
	//memset(f,-1,sizeof(f));
	f[0][0]=1;
	for(int i=1; i<=len; i++){
		f[i][0]=1;
		for(int j=1; j<=i; j++) f[i][j]=(f[i-1][j]+f[i-1][j-1])%Phi;
	}
	for(int i=2; i<=len; i++)
		ans = ans*Pow(i, dfs(len, 1, i) )%P;
	printf("%lld", ans);
}
posted @ 2017-03-27 21:47  Candy?  阅读(402)  评论(0编辑  收藏  举报