洛谷 子集积 题解

题目

P1 背包

子集积 > m >m >m 的个数并不好求,考虑子集积 ≤ m \le m m 的个数 x x x,答案即为 ( 2 n − x ) (2^n - x) (2nx)

对于子集积 ≤ m \le m m 的个数,可以化为 0-1 背包问题做, f i , j f_{i,j} fi,j 表示前 i i i 个数,子集积为 j j j 的个数,有:

f i , j = ∑ j = 1 m f i − 1 , j a i f_{i,j}=\sum \limits_{j=1}^{m} f_{i-1,\frac {j} {a_i}} fi,j=j=1mfi1,aij j j j a i a_i ai 的倍数)。

背包问题常规地去掉一维: f j f_j fj 表示子集积为 j j j 的个数:

f j = ∑ j = 1 m f j a i f_j=\sum \limits_{j=1}^{m} f_{\frac {j} {a_i}} fj=j=1mfaij j j j a i a_i ai 的倍数)。

	cin >> n >> m;
	
	for(int i=1; i<=n; i++) cin >> a[i];
	
	f[1] = 1;
	for(int i=1; i<=n; i++)
		for(int j=(m / a[i]) * a[i]; j>=a[i]; j-=a[i])
			f[j] += f[j / a[i]], f[j] %= mod;
		
	int sum = qpow(2, n);
	for(int i=1; i<=m; i++)
		sum -= f[i],  sum = (sum % mod + mod) % mod;
	cout << sum;

时间复杂度 O ( ∑ i = 1 n m a i ) O( \sum\limits_{i=1}^{n} {\frac {m} {a_i}}) O(i=1naim),最坏情况下 O ( n m ) O(nm) O(nm)

P2 优化

优化 1

若序列中有 100 100 100 1 1 1,然而任意多个 1 1 1 不会对子集积产生影响,我们只需要在方案数中乘以 2 100 2^{100} 2100 即可。

	...
	int sum = qpow(2, n);
	for(int i=1; i<=m; i++)
		sum -= f[i] * qpow(2, cnt[1]) % mod,  sum = (sum % mod + mod) % mod;
	cout << sum;

优化 2

时间复杂度高的原因在于重复的计算:若有 100 100 100 2 2 2,我们将第 2 , 3 2,3 2,3 2 2 2、第 3 , 4 3,4 3,4 2 2 2、第 4 , 5 4,5 4,5 2 2 2 等等算了多次。我们应该只关心是几个 2 2 2,而不关心是哪几个 2 2 2

对于任意一个数 x x x,设其出现了 t t t 次,我们可以对 x 1 , x 2 , . . . , x t x^1,x^2,...,x^t x1,x2,...,xt 分别计算,使用 x i x^i xi 计算贡献时乘以 ( t i ) \binom {t}{i} (it), 即 :

f j = ∑ i = 1 t ( f j x i × ( t i ) ) f_j=\sum\limits_{i=1}^{t} ( f_{\frac {j} {x^i}} \times \binom {t}{i}) fj=i=1t(fxij×(it)) j j j x k x^k xk 的倍数)。

V V V 为值域。时间复杂度在 a i a_i ai 各不相同时为 O ( ∑ i = 1 V V i ) = O ( V ln ⁡ V ) O(\sum\limits_{i=1}^{V} \frac {V}{i}) = O(V \ln V) O(i=1ViV)=O(VlnV),在 a i a_i ai 全部为 2 2 2 时为 O ( m log ⁡ m ) O(m \log m) O(mlogm) n , m , V n,m,V n,m,V 同阶时均摊为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

注意: 这里与多重背包的二进制拆分拆成多个物品不同,是优化了对于一个物品的计算方式。

代码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值