1. 程式人生 > >【BZOJ3209】花神的數論題 數位DP

【BZOJ3209】花神的數論題 數位DP

for led sof 又一 拆分 nbsp return 範圍 題目

【BZOJ3209】花神的數論題

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

題解:又一個題目名稱和題本身一點關系都沒有的~

很容易想到按位拆分,分別考慮1的個數是k的數有多少個,然後快速冪一下計算貢獻

怎麽知道1的個數是k的數有多少個呢?預處理出組合數,然後數位DP吧!(對本蒟蒻來說就是INF的細節)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll mod=10000007;
ll c[60][60];
ll cnt[60];
ll n,sum,ans;
ll pm(ll x,ll y)
{
	ll z=1;
	while(y)
	{
		if(y&1)	z=z*x%mod;
		x=x*x%mod,y>>=1;
	}
	return z;
}
int main()
{
	c[0][0]=1;
	ll i,j;
	for(i=1;i<=50;i++)
	{
		c[i][0]=1;
		for(j=1;j<=i;j++)	c[i][j]=c[i-1][j-1]+c[i-1][j];
	}
	scanf("%lld",&n);
	for(i=50;i;i--)
	{
		if(n&(1ll<<i-1))
		{
			for(j=sum;j<=50;j++)	cnt[j]+=c[i-1][j-sum];
			sum++;
		}
	}
	cnt[sum]++;
	for(ans=i=1;i<=50;i++)	ans=ans*pm(i,cnt[i])%mod;
	printf("%lld",ans);
	return 0;
}

【BZOJ3209】花神的數論題 數位DP