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

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

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

由於N<=1015N<=10^{15}知這個範圍內的數二進位制下最多隻有50個1 所以我們列舉1的個數KK,分別統計1~N內有多少個數二進位制下恰好有KK個1 最後用快速冪累加進答案

這裡的數位DP需要在二進位制下進行 dp[i][j]dp[i][j]表示 當前處理到第ii位,且已經出現了jj個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;
}