BZOJ3209 || P4317 花神的數論題【數位DP】
阿新 • • 發佈:2018-12-12
Time Limit: 10 Sec Memory Limit: 128 MB
Description
眾所周知,花神多年來憑藉無邊的神力狂虐各大 OJ、OI、CF、TC …… 當然也包括 CH 啦。 話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。 花神的題目是這樣的 設 sum(i) 表示 i 的二進位制表示中 1 的個數。給出一個正整數 N ,花神要問你 派(Sum(i)),也就是 sum(1)—sum(N) 的乘積。
Input
一個正整數 N。
Output
一個數,答案模 10000007 的值。
HINT
對於 100% 的資料,N≤10^15
題目分析
什麼鬼數論明明就是DP
這裡的數位DP需要在二進位制下進行 表示 當前處理到第位,且已經出現了個1的滿足條件的數字個數 只需要套數位DP的板就好了
lt DP(int len,int cnt,int pre)
{
if(len==0) return (cnt==K);
if(!pre&&dp[len][ cnt]!=-1) return dp[len][cnt];
lt res=0,mx=pre?dig[len]:1;
for(int i=0;i<=mx;++i)
{
if(cnt+(i==1)>K) continue;
res+=DP(len-1,cnt+(i==1),pre&&i==mx);
}
if(!pre) dp[len][cnt]=res;
return res;
}
lt solve(lt x)
{
int len=0;
while(x)
{
dig[ ++len]=x&1;
x>>=1;
}
return DP(len,0,1);
}
完整程式碼
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int mod=10000007;
const int maxn=50;
lt n,K,ans=1;
lt dig[maxn],dp[maxn][maxn];
lt qpow(lt a,lt k,lt p)
{
lt res=1;
while(k>0){
if(k&1) res=(res*a)%p;
a=(a*a)%p; k>>=1;
}
return res;
}
lt DP(int len,int cnt,int pre)
{
if(len==0) return (cnt==K);
if(!pre&&dp[len][cnt]!=-1) return dp[len][cnt];
lt res=0,mx=pre?dig[len]:1;
for(int i=0;i<=mx;++i)
{
if(cnt+(i==1)>K) continue;
res+=DP(len-1,cnt+(i==1),pre&&i==mx);
}
if(!pre) dp[len][cnt]=res;
return res;
}
lt solve(lt x)
{
int len=0;
while(x)
{
dig[++len]=x&1;
x>>=1;
}
return DP(len,0,1);
}
int main()
{
n=read();
for(int i=1;i<=50;++i)
{
memset(dp,-1,sizeof(dp));
K=i; ans=(ans*qpow(i,solve(n),mod))%mod;
}
printf("%lld",ans);
return 0;
}