洛谷P4317
阿新 • • 發佈:2020-12-01
Description
定義 \(sum(i)\) 表示 \(i\) 的二級制中 1 的個數
給定一個 N,求 \(\prod_{i=1}^N sum(i)\)
Solution
顯然是數位 DP
考慮 DP 陣列應儲存的資訊以及維數
設 \(f[i][j][k]\) 表示列舉到第 i 位,數字為 j,此時二進位制中 1 的個數為 k
由 Suzt_ilymtics 的口胡和 Aliemo 的證明可得轉移方程和限制條件:
\[f[i][0][j]=\sum_{k=0}^if[i-1][1][k]+f[i-1][0][k] \]\[f[i][1][j]=\sum_{k=1}^if[i-1][0][k-1]+f[i-1][1][k-1] \]然後,對於答案的統計
對於首位為 1 且前面有 \(cnt\) 個 1 的 \(f\) ,
\[ans=ans\times (j+cnt)^{f[i][0][j]}\mid i\in[1,len),j\in[1,len/2-cnt] \]而對於首位為 0 的 \(f\),
\[ans=ans\times j^{f[i][1][j]}\mid i\in [1,len),j\in[1,i/2] \]最後答案就是 \(ans_{1,N}\)
自己在做題過程中有許多錯誤,總結一下:
- 注意邊界問題和初始化問題
- 注意什麼地方該取模(統計)
- 注意限制條件的判斷以及迴圈中上下界的大小
- 壓行什麼得確保沒問題了再說,避免 Debug 困難
Code
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<queue> #include<cmath> #include<cstring> #define Mod 10000007 #define int long long using namespace std; int a[50]; int n,f[80][2][80]; inline int read(){ int s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar(); return w*s; } inline int Pow(int x,int y){ int ans=1; while(y){if(y&1)ans=(ans*x)%Mod;x=(x*x)%Mod;y>>=1;} return ans%Mod; } inline void init(){ f[1][1][1]=1;f[1][0][0]=1; for(register int i=2;i<64;i++) for(register int j=0;j<=1;j++) for(register int k=0;k<=i;k++) for(register int s=0;s<=1;s++){ if(j==0) f[i][j][k]+=f[i-1][s][k]; if(j==1&&k!=0) f[i][j][k]+=f[i-1][s][k-1]; } } inline int solve(int x){ memset(a,0,sizeof a); int len=0,cnt=1,ans=0; while(x){a[++len]=x%2;ans+=a[len],x/=2;} for(register int i=len-1;i>=1;i--){ if(a[i]) for(register int j=0;j<=len-cnt;j++) if(f[i][0][j]!=0) ans=(ans*Pow(j+cnt,f[i][0][j]))%Mod; cnt+=a[i]; } for(register int i=1;i<len;i++) for(register int j=1;j<=i;j++) if(f[i][1][j]!=0) ans=(ans*Pow(j,f[i][1][j]))%Mod; return ans%Mod; } signed main(){ n=read();init(); printf("%lld",solve(n)); return 0; }