花神的数论题 bzoj-3209

题目大意:sum(i)表示i的二进制表示中1的个数,求$\prod\limits_{i=1}^n sum(i)$

注释:$1\le n\le 10^{15}$。

想法:喷一下题目...神tm数论题,明明是个dp。

显然,如果稍微打个表的话就可以发现,有很多数的sum是相等的,我们不想重复乘这么多次,所以我们想到将所有sum相等的数弄到一起然后快速幂。这样,就不难想到数位dp

状态:dp[i][j]表示i位,sum值是j的个数。

转移是容易的,按照数位dp的边界特判就行了。

最后,附上丑陋的代码... ...

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long int ll;
const int MAXN=60+5;
const ll mod=10000007;
ll n, Ans;
ll C[MAXN][MAXN];
int l,wei[MAXN];
void pre()
{
	for (int i=0;i<=60;++i)
		C[i][0]=1;
	for(int i=1;i<=60;i++)
		for(int j=1;j<=i;++j)
			C[i][j]=C[i-1][j-1]+C[i-1][j];
}
ll Solve(int x)
{
	ll sum=0;
	for(int i=l;i>=1;i--)
	{
		if(wei[i]==1)
		{
			sum+=C[i-1][x];
			--x;
		}
		if(x<0) break;
	}
	return sum;
}
ll quick_power(ll x,ll y)
{
	ll ans=1;
	while(y)
	{
		if(y&1) ans=(ans*x)%mod;
		y>>=1;
		x=(x*x)%mod;
	}
	return ans;
}
int main()
{
	pre();
	scanf("%lld",&n);
	++n;
	l=0;
	while(n)
	{
	  wei[++l]=n&1;
	  n>>=1;
	}
	Ans=1ll;
	for(int i=1;i<=l;i++)
	{
		Ans=Ans*quick_power(i,Solve(i))%mod;
	}
	printf("%lld\n",Ans);
	return 0;
}

小结:有意思...别被题面迷惑了(@EdwardFrog)