P6189 [NOI Online #1 入門組] 跑步(分拆數)

dcytrl發表於2024-10-21

簡要題意

給你一個整數 \(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);
}

相關文章