题意:给出$N$表示背包容量,且会给出$N$种物品,第$i$个物品大小为$i$,数量也为$i$,求装满这个背包的方案数,对$23333333$取模。$N \leq 10^5$
$23333333=17 \times 1372549$竟然不是质数性质太不优秀了(雾
直接跑背包$O(N^2)$,于是咱们考虑挖掘性质、分开计算
发现当$i < \sqrt{N}$时就是一个多重背包,用单调队列优化到$O(N \sqrt{N})$
而当$i \geq \sqrt{N}$时,选中物品的数量不会超过$\sqrt{N}$,又因为最小的物品大小为$\sqrt{N}$,所以我们可以设计一个DP:
设$dp_{i,j}$为放了$i$个物品,其中物品总重量为$j$时的方案总数,有
$$dp_{i,j}=dp_{i-1,j-\sqrt{N}}+dp_{i,j-i}$$
也就是两种转移:①多加一个$\sqrt{N}$②给所有物品$+1$
总复杂度为$O(N\sqrt{N})$
1 #include<bits/stdc++.h>
2 #define MOD 23333333
3 #define MAXN 100001
4 using namespace std;
5
6 int f[MAXN] = {1} , g[2][MAXN] = {1} , allG[MAXN] = {1};
7
8 int main(){
9 int N , T , ans = 0 , dir = 0;
10 cin >> N;
11 T = sqrt(N);
12 for(int i = 1 ; i <= T ; i++)
13 for(int j = 0 ; j < i ; j++){
14 int sum = 0 , t = N / i * i + j;
15 if(t > N)
16 t -= i;
17 int now = t;
18 for(int k = 0 ; k <= i && t >= 0 ; k++){
19 sum = (sum + f[t]) % MOD;
20 t -= i;
21 }
22 while(now >= 0){
23 int q = f[now];
24 f[now] = sum;
25 sum = (sum - q + MOD) % MOD;
26 now -= i;
27 if(t >= 0){
28 sum = (sum + f[t]) % MOD;
29 t -= i;
30 }
31 }
32 }
33 for(int i = 1 ; i <= T ; i++){
34 dir ^= 1;
35 memset(g[dir] , 0 , sizeof(g[dir]));
36 for(int j = T + 1 ; j <= N ; j++){
37 g[dir][j] = (g[dir ^ 1][j - (T + 1)] + g[dir][j - i]) % MOD;
38 allG[j] = (allG[j] + g[dir][j]) % MOD;
39 }
40 }
41 for(int i = 0 ; i <= N ; i++)
42 ans = (ans + (long long)allG[i] * f[N - i]) % MOD;
43 cout << ans;
44 return 0;
45 }