首先这是一个多重背包
但是它的数据非常特殊,我们可以利用其性质优化算法
一个显然的优化是
当(i>sqrt n)时,可以取消个数限制
设(f[i][j])表示选了(i)个物品,体积为(j)的方案数
一共有两种转移
可以由(i-1)个物品加上一个最小的物品(sqrt n+1)
可以由(i)个物品全部加一(全部换成下一个)
即(f[i][j]=f[i-1][j-(sqrt n+1)]+f[i][j-i])
考虑得到一个答案的过程
对于每一个过程都会得到一个合法的答案
对于每一个合法的答案都有唯一一个过程
因为对于每一个方案
如果其中存在最小的物品
那么一定是由第一种方案转移
否则一定是由第二种方案转移
比如(n=16)
体积为(16)的一种方案是(5+5+6)
这个状态一定由(5+6)转移来
因为(4=sqrt 16),比最小的物品还小
所以(4+4+5)是不可能的
体积为(12)的一种方案是(6+6)
这个状态一定由(5+5)转移来
因为方案里并没有(5)(最小的物品)
所以方案与过程一一对应
复杂度(O(nsqrt n))
剩下的是多重背包
(f[i][j]=sum_{k=0}^{i}{f[i-1][j-i*k]})
按模(i)分类
复杂度(O(nsqrt n))
最后枚举多少空间给前(sqrt n)个物品(剩下空间给其它物品)
统计答案即可
复杂度(O(nsqrt n))
#include<bits/stdc++.h>
using namespace std;
#define gc c=getchar()
#define r(x) read(x)
template<typename T>
inline void read(T&x){
x=0;T k=1;char gc;
while(!isdigit(c)){if(c=='-')k=-1;gc;}
while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}
const int p=23333333;
const int N=1e5+7;
int sum[N],f1[2][N],f2[2][N];
inline int add(int a,int b){
a+=b;
if(a>=p)a-=p;
return a;
}
int main(){
int n;r(n);
int t=sqrt(n);
f1[0][0]=1;
for(int i=1;i<=t;++i){
for(int j=0;j<=n;++j){
sum[j%i]=add(sum[j%i],f1[i&1^1][j]);
if(j-i*(i+1)>=j%i)sum[j%i]=add(sum[j%i],p-f1[i&1^1][j-i*(i+1)]);
f1[i&1][j]=sum[j%i];
}
memset(sum,0,i<<2);
}
memset(sum,0,(n+1)<<2);
f2[0][0]=1;
for(int i=1;i<=t;++i){
memset(f2[i&1]+(i-1)*(t+1),0,(t+1)<<2);
for(int j=i*(t+1);j<=n;++j){
f2[i&1][j]=add(f2[i&1][j-i],f2[i&1^1][j-(t+1)]);
sum[j]=add(sum[j],f2[i&1][j]);
}
}
sum[0]=1;
long long ans=0;
for(int i=0;i<=n;++i)(ans+=1ll*f1[t&1][i]*sum[n-i])%=p;
printf("%lld
",ans);
}