Legend
Link ( extrm{to Luogu})。
求 (n) 的拆分数。
(1 le n le 10^5)。
Editorial
Simple (O(n sqrt{n}))
考虑到一个事实:(le sqrt{n}) 的数字只有 (O(sqrt{n})) 个,可以暴力完全背包。
完全背包显然没有人不会。复杂度 (O(n sqrt{n}))
考虑到另一个事实:(>sqrt{n}) 的数字最多只会被选 (O(sqrt{n})) 个,可以暴力 (dp)。
怎么 (dp)?设 (dp_{i,j}) 为把 (j) 拆分成 (i) 个数字的方案数,总状态数是 (O(n sqrt{n})) 的。
转移有 (dp_{i,j}=dp_{i-1,j-(sqrt{n}+1)}+dp_{i,j-i}) 两种。
含义分别为选择一个 (sqrt{n}+1) 大小的物体和把所有物体高度增加 (1)。这样可以涵盖所有情况。
这一部分复杂度也是 (O(n sqrt{n}))。
再用 (O(n)) 合并两个背包的答案,总复杂度 (O(n sqrt{n}))。
$O(n log n) $
一切的前提,模数要是 ( m{NTT}) 模数。显然本题用不了这个做法。
考虑毒瘤的生成函数做法:
拆分数的生成函数显然为 (F(x)=prodlimits_{i=1}^{infty} sumlimits_{j=0}^{infty}(x^i)^j)。
根据 (sum_{i=0}^{infty} x^i=frac{1}{1-x}) 可以得出 (F(x)=prodlimits_{i=1}^{infty} dfrac{1}{1-x^i})。
两边同时取 (ln) 有 (ln F(x)= -sumlimits_{i=1}^{infty}ln (1-x^i))。
现在的问题就是如何计算 (h(x)=ln(1-x)),因为 (ln F(x)= -sumlimits_{i=1}^{infty}h(x^i))。
求个导数 (h'(x)=-frac{1}{1-x}=-sumlimits_{i=0}^{infty}x^i)。
同时积分有 (h(x)= - sumlimits_{i=1}^{infty}frac{x^i}{i}),求完了。
带回 (F(x)) 有 (ln F(x)= sumlimits_{i=1}^{infty}sumlimits_{j=1}^{infty}frac{x^{ij}}{j}=sumlimits_{t=1}^{infty}x^t sumlimits_{j|t}frac{1}{j})。
显然这个式子可以调和级数 (O(n log n)) 预处理完。
再把式子 (exp) 回去就做完了。复杂度就是 (O(n log n))。
Code
采用的根号分治做法。
#include <bits/stdc++.h>
#define debug(...) fprintf(stderr ,__VA_ARGS__)
using namespace std;
int n ,p;
const int MX = 1e5 + 23;
const int SIZE = 320;
int dp[MX];
int f[SIZE][MX] ,dp2[MX];
int main(){
cin >> n >> p;
dp[0] = 1 % p;
for(int i = 1 ; i <= min(n ,SIZE) ; ++i){
for(int j = i ; j <= n ; ++j){
dp[j] = (dp[j] + dp[j - i]) % p;
}
}
dp2[0] = f[0][0] = 1 % p;
for(int i = 1 ; i < SIZE ; ++i){
for(int j = SIZE + 1 ; j <= n ; ++j){
f[i][j] = (f[i - 1][j - (SIZE + 1)] + f[i][j - i]) % p;
dp2[j] = (dp2[j] + f[i][j]) % p;
}
}
int Ans = 0;
for(int i = 0 ; i <= n ; ++i){
Ans = (Ans + 1LL * dp[i] * dp2[n - i]) % p;
}
printf("%d
" ,Ans);
return 0;
}