bzoj3209 花神的数学题

3209: 花神的数论题

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1612   Solved: 746
[ Submit][ Status][ Discuss]

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

Source

原创 Memphis




数位DP+数论

令f[x]表示sum[i]=x的i的个数,则最终答案等于(1^f[1])*(2^f[2])*(3^f[3])*……

因为底数的范围很小,而10000007=941*10627,所以底数和10000007一定互质,根据欧拉定理,只要将指数对phi(10000007)=9988440取模就可以了。

那现在问题就是如何求f[x]。

从高位向低位进行数位DP,具体方法见程序。

为什么各种各样的DP自己都想不出来...(╯‵□′)╯︵┻━┻




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define mod 10000007
#define phi 9988440
using namespace std;
ll n,ans=1,c[60][60],f[60];
inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch>='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void dp(ll x)
{
	int tmp=0,cnt=0;ll now=0;
	while ((1ll<<tmp)<x) tmp++;
	D(i,tmp,0) if (now+(1ll<<i)<=x)
	{
		F(j,0,i) (f[j+cnt]+=c[i][j])%=phi;
		cnt++;now+=(1ll<<i);
	}
}
inline ll getpow(ll x,ll y)
{
	ll ret=1;
	for(;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;
	return ret;
}
int main()
{
	scanf("%lld",&n);
	F(i,0,50)
	{
		c[i][0]=1;
		F(j,1,i) c[i][j]=(c[i-1][j-1]+c[i-1][j])%phi;
	}
	dp(n+1);
	F(i,1,50) ans=ans*getpow(i,f[i])%mod;
	printf("%lld\n",ans);
}


你可能感兴趣的:(bzoj,数位dp)