LOJ6089 小Y的背包计数问题 背包、根号分治

题目传送门

题意:给出$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 }

 

转载于:https://www.cnblogs.com/Itst/p/9784162.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值