簡要題意
給你一個整數 \(n\),你需要求 \(\sum_{i=1}^n x_i=n\) 且 \(x_i\le x_{i+1}\) 的非負整數解數量對給定模數 \(p\) 取模後的結果。
\(n\le 10^5\)
分析
考慮一個顯然的 DP:設 \(f_{i,j}\) 表示考慮 \(1\sim i\) 這些數,總和為 \(j\) 的方案數。轉移是完全揹包型轉移:\(f_{i,j}=f_{i-1,j}+f_{i,j-i}\)。時間複雜度 \(O(n^2)\),飛了。
考慮最佳化。
注意到當取的數較大的時候,取的數不會太多。
考慮根號分治,設閾值 \(B=\sqrt n\),那麼 \(>B\) 的數選的數的個數 \(\le B\)。設 \(g_{i,j}\) 表示選了 \(i\) 個數,選的數的和是 \(j\) 的方案數。我們在選數的時候,先固定選了 \(i\) 個 \(B\),然後每次考慮若要選更大的數,我們就給當前的數全域性加 \(1\)。那麼寫成轉移方程就是 \(g_{i,j}=g_{i-1,j-B}+g_{i,j-i}\)。結合暴力 DP 時間複雜度 \(O(n\sqrt n)\)。
int n,mod;
int f[maxm][maxn],g[maxm][maxn];
int F[maxn],G[maxn];
void add(int &x,int y){x+=y,x=x>=mod?x-mod:x;}
void solve_the_problem(){
rd(n),rd(mod);
f[0][0]=1;
rep(i,1,B-1)rep(j,0,n){
add(f[i][j],f[i-1][j]);
if(j>=i)add(f[i][j],f[i][j-i]);
}
rep(i,0,n)F[i]=f[B-1][i];
g[0][0]=G[0]=1;
rep(i,1,B)rep(j,0,n){
if(j>=B)add(g[i][j],g[i-1][j-B]);
if(j>=i)add(g[i][j],g[i][j-i]);
add(G[j],g[i][j]);
}
int ans=0;
rep(i,0,n)ans=(ans+1ll*F[i]*G[n-i]%mod)%mod;
write(ans);
}