题面
题解
我 TM 以为是一道多项式题
考虑到这样一个性质
对于每个编号大于 (sqrt{n}) 的物品, 我们不会选超过 (sqrt {n}) 个
所以我们把整个选择分为两部分来看
第一部分是编号小于等于 (sqrt{n}) 的
我们设 (f[i][j]) 代表选了编号前 (i) 个的, 体积总和为 (j) 的方案数
前缀和优化一下就行
第二部分是编号大于 (sqrt{n}) 的
我们可以将选择变成这样两种操作
- 把选择的 (i) 个数全部加一
- 加入一个大小为 (sqrt {n} + 1) 的数
可以证明这两个操作可以把编号大于 (sqrt{n} + 1) 的选择方案全部不重不漏的表达出来
没了, 两个乘起来就行了
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
const int mod = 23333333;
const int N = 1e5 + 5;
using namespace std;
int n, m, f[405][N], g[405][N], s[N], ans;
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
int main()
{
n = read <int> (), m = sqrt(n);
for(int i = 0; i <= m; i++)
f[i][0] = 1;
for(int i = 1; i <= m; i++)
{
for(int j = 0; j <= n; j++)
s[j] = ((j - i >= 0 ? s[j - i] : 0) + f[i - 1][j]) % mod;
for(int j = 0; j <= n; j++)
f[i][j] = (s[j] - (j - i * i - i >= 0 ? s[j - i * i - i] : 0) + mod) % mod;
}
memset(s, 0, sizeof(s)), g[0][0] = 1;
for(int i = 1; i <= m; i++)
for(int j = 0; j <= n; j++)
{
if(j - i > 0) g[i][j] = g[i][j - i];
if(j - m > 0) g[i][j] = (g[i][j] + g[i - 1][j - m - 1]) % mod;
s[j] = (s[j] + g[i][j]) % mod;
}
for(int i = 0; i <= n; i++)
ans = (1ll * ans + 1ll * s[n - i] * f[m][i] % mod) % mod;
ans = (1ll * ans + f[m][n]) % mod;
printf("%d
", ans);
return 0;
}