花神的数论题

洛咕

题意:设\(\text{sum}(i)\)表示\(i\)的二进制表示中\(1\)的个数.给出一个正整数\(N\),求\(\prod_{i=1}^{N}\text{sum}(i)\),也就是 \(\text{sum}(1)\sim\text{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\n",part(n));
    return 0;
}

posted on 2019-09-25 19:25  PPXppx  阅读(159)  评论(0)    收藏  举报