1. 程式人生 > >BZOJ 3209: 花神的數論題 數位DP

BZOJ 3209: 花神的數論題 數位DP

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

題解

挺傻的一道數位DP,f[60][60][2]代表到達第i位,二進位制中有j個1,是否頂位的方案數,最後快速冪乘一下即可。

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<ctime>
#include<cmath>
#include<cstring>
#include<string>
using namespace std; long long f[70][70][2]; const int mod=1e7+7; long long ksm(long long x,long long t) { long long re=1; while(t) { if(t&1) re=re*x%mod; x=x*x%mod; t>>=1; } return re; } void dp(long long x) { f[0][0][1]=1; for(int i=0;i<=60
;i++) for(int j=0;j<=60;j++) for(int k=0;k<=1;k++) { if(!f[i][j][k]) continue; for(int kk=0;kk<=1;kk++) { if(k) { if(!(x&(1ll<<(60-i)))) { if(kk==1) continue; f[i+1][j+kk][1]+=f[i][j][k]; } else { if(kk==0) f[i+1][j+kk][0]+=f[i][j][k]; else f[i+1][j+kk][1]+=f[i][j][k]; } } else f[i+1][j+kk][k]+=f[i][j][k]; } } long long ans=1; for(int i=1;i<=60;i++) { ans*=ksm(i,f[61][i][0]+f[61][i][1]); ans%=mod; } cout<<ans<<endl; } int main() { long long x; scanf("%lld",&x); dp(x); return 0; }