【數位DP】bzoj3209
阿新 • • 發佈:2018-12-30
【題目描述】
話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。
花神的題目是這樣的
設 sum(i) 表示 i 的二進位制表示中 1 的個數。給出一個正整數 N ,花神要問你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘積取膜10^7+7的值。
【思路】數位DP,考慮出現了一個1,兩個1,三個1....的數。先預處理一下C[i][j],表示從i位裡面選j個1出來。
C[i][j]=C[i-1][j]+C[i-1][j-1]。
然後列舉前i-1位和N相同,如果N的第i位為1,這時就把第i位填成0,後面的就可以瞎填了。如果N的第i位為0,那這一位只能填0,就是前i位和N相同了。
實現的時候記錄一下之前出現了多少個1。
#include<bits/stdc++.h> #define ll long long using namespace std; const int mod=10000007; ll n,C[65][65],ans=1,cnt[65]; int len=-1,pre; inline ll quick_pow(ll a,ll b){ll ret=1;for(;b;b>>=1,a=a*a%mod)if(b&1)ret=ret*a%mod;return ret;} void work(){ for(int i=0;i<=60;++i){ C[i][0]=1; for(int j=1;j<=i;++j) C[i][j]=C[i-1][j-1]+C[i-1][j]; } } signed main(){ cin>>n,work(); while((1ll<<(++len))<=n); for(int i=len-1;i>=0;--i) //前【len-i-1】位相同,看第【len-i】位是否為1.如果是1,那這一位填0,後面i個就可以隨便填了。 if((n>>i)&1){ for(int j=0;j<=i;++j) cnt[j+pre]+=C[i][j]; //pre記錄前面出現多少個1。 ++pre; }++cnt[pre];//最後要算上N自己的貢獻 for(int i=2;i<=len;++i)(ans*=quick_pow(i,cnt[i]))%=mod; cout<<ans; return 0; }