题意:设( ext{sum}(i))表示(i)的二进制表示中(1)的个数.给出一个正整数(N),求(prod_{i=1}^{N} ext{sum}(i)),也就是 ( ext{sum}(1)sim ext{sum}(N))的乘积.(N<=10^{15}.)
分析:做完同类分布之后再做的这题,所以一下就想到了要枚举 二进制表示中含(1)的个数,然后分别求有多少个.
一开始想错结论了,就把每次(dfs)的结果都累乘起来,发现答案基本上都是零.假设我们当前枚举的是二进制表示中含(1)的个数有(sum)个,然后有(x)个这样的数,则此次的贡献是(sum^x).
对于模数(1e7+7),好像这不是个质数,(1e7+7=941*10627),但是好像不用管这个,还是照常取模就行了.是因为数据水么(???)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int mod=10000007;
int len,a[60];ll ans=1,dp[60][60][60];
inline ll dfs(int pos,int pre,int sum,int lead,int limit,int goal){
if(pos>len)return sum==goal;
if(sum>goal)return 0;//剪枝:如果1的个数超过了当前枚举
if(dp[pos][pre][sum]!=-1&&!lead&&!limit)return dp[pos][pre][sum];
ll cnt=0;int res=limit?a[len-pos+1]:1;
for(int i=0;i<=res;++i){
if((!i)&&lead)cnt+=dfs(pos+1,0,0,1,limit&&(i==res),goal);
else if(i&&lead)cnt+=dfs(pos+1,i,(i==1),0,limit&&(i==res),goal);
else cnt+=dfs(pos+1,i,sum+(i==1),0,limit&&(i==res),goal);
}
return !lead&&!limit?dp[pos][pre][sum]=cnt:cnt;
}
inline ll ksm(ll a,ll b){
ll cnt=1;
while(b){
if(b&1)cnt=(1ll*cnt*a)%mod;
a=(1ll*a*a)%mod;
b>>=1;
}
return cnt;
}
inline ll part(ll x){
len=0;while(x)a[++len]=x&1,x>>=1;//拆成二进制表示
for(int sum=1;sum<=len;++sum){//枚举含1的个数
memset(dp,-1,sizeof(dp));
ans=(1ll*ans*ksm(sum,dfs(1,0,0,1,1,sum)))%mod;//统计答案
}
return ans;
}
int main(){
ll n=read();printf("%lld
",part(n));
return 0;
}