考虑根号分治,前面是个多重背包,直接分组部分和优化。
然后后面是个完全背包,直接做容易炸,但直接上整数划分dp即可。
#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
#define M 810
#define mo 23333333
#define N 200010
void Mod(int &a) { if(a>=mo || a<=-mo) a%=mo; if(a<0) a+=mo; }
void Add(int &a, int b) { a+=b; Mod(a); }
int n, m, i, j, k, T;
int g[M][N], f[2][N], ans, s[N];
signed main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// srand(time(NULL));
// T=read();
// while(T--) {
//
// }
n=read(); m=sqrt(n)+1;
debug(">> %lld\n", m);
f[0][0]=1;
for(i=1; i<m; ++i) {
for(j=0; j<=n; ++j) s[j]=f[i&1][j]=0;
for(j=0; j<=n; ++j) {
Add(s[j%i], f[(i-1)&1][j]);
if(j-i*(i+1)>=0) Add(s[j%i], -f[(i-1)&1][j-i*(i+1)]);
f[i&1][j]=s[j%i];
}
}
for(j=0; j<=n; ++j) debug("%lld ", f[(m-1)&1][j]); debug("\n");
g[1][m]=1;
for(i=0; i<=m+5; ++i)
for(j=0; j<=n; ++j) {
if(g[i][j]) debug("g[%lld][%lld] = %lld\n", i, j, g[i][j]);
if(j+m<=n) Add(g[i+1][j+m], g[i][j]);
if(i+j<=n) Add(g[i][i+j], g[i][j]);
}
for(i=0; i<=n; ++i) s[i]=0; s[0]=1;
for(i=0; i<=m+5; ++i) for(j=0; j<=n; ++j) Add(s[j], g[i][j]);
for(i=0; i<=n; ++i) Add(ans, f[(m-1)&1][i]*s[n-i]);
printf("%lld", ans);
return 0;
}