若干背包模型 powered by ddxrS

同余最短路与完全背包

经典问题是 luogu P9140,类似题目还有 CF 2115E

给定若干物品 \((v_i,w_i)\),其中 \(w\) 是价值 \(v\) 是占用体积,做完全背包。

不妨默认已经按性价比排序。

其核心结论有如下几个:

  1. 至多有 \(v^2_{\max}\) 的背包用于放置非第一个物品。

    证明:将完全背包看做同余最短路,将每个 \(i\) 连向 \((i+v_j)\bmod v_1\) ,边权为 \(w_i-\lfloor\frac{i+v_j}{v_1}\rfloor w_1\)

    因为是选取的性价比最高的在外面,因此不存在负环,那么到任意点的所经过边数不超过 \(v_1\),而每条边最多选取 \(v_{\max}\),因此转移到每个点实际上最多选取了 \(v_1v_{\max}\le v^2_{\max}\) 的体积。

  2. 使用转圈法优化同余最短路

    常规同余最短路写法是 SPFA,但可以使用转圈法优化。

    将同余最短路看做一个环形dp。

    逐个考虑物品 \((v_i,w_i)\),此刻转移图会分为 \(\gcd(v_i,v_1)\) 个环。

    根据最短路,我们不会重复经过点多次,因此我们只需要从每个环的任意点开始转两圈更新即可,这样足以涵盖所有情况。

    	int M=a[1].first,C=a[1].second;
    	for(int i=2;i<=n;++i){
    		int w=a[i].first,v=a[i].second;
    		for(int j=0,lim=__gcd(w,M);j<lim;++j){
    			for(int c=0,t=j;c<2;c+=(j==t)){
    				int d=(t+w)/M;
    				int nt=t+w-d*M;
    				f[nt]=max(f[nt],f[t]+v-C*d);t=nt;
    			}
    		}
    	}	
    

完全背包可行性

选择物品个数

仍然用 \(v_i\) 表示。

引理:任意一个可能被表达出的 \(M\),其最多用到 \(\log M\) 个不同物品即可表达出 \(M\),且表达的最小字典序满足此性质。

证明:设一个选取方案 \(\overline{c}=(c_1,c_2\dots c_n)\)\(\sum c_iv_i=M\)

\(\overline{c}\) 视作一个数组,进行字典序比对,则字典序最小的方案一定满足 \(\prod (c_i+1)\le M+1\)

反证法,如果 \(>M+1\),根据鸽巢原理,有总方案数 \(\prod (c_i+1)>M+1\),其方案体积和在 \([0,M]\), 则必然存在两个体积和相同的子方案 \(\overline{c_1},\overline{c_2}\) 同时二者不同时取同一个物品。

\(\overline{c}-\overline{c_1}+\overline{c_2}\)\(\overline{c}-\overline{c_2}+\overline{c_1}\) 其中字典序更小的方案会比原方案字典序更小。(考虑两个子方案第一个不同的位置)。

因此若有 \(n\) 个物品,找出方案可以 \(O(m{n\choose \log n})\) 解决,如果 \(n\) 比较小,还可以由此做子集和算出每个物品子集是否可行。

引理:对于带价值的问题,同样满足存在一个价值最大的最小字典序方案满足此性质。

证明关键在于 \(\overline{c}-\overline{c_1}+\overline{c_2}\)\(\overline{c}-\overline{c_2}+\overline{c_1}\) 两个方案的权值肯定与 \(\overline{c}\) 一个 $\ge $ 一个 \(\le\)

若假定 \(\overline{c}\) 最大,则必然取 \(=\),则逐步调整即可证明。

多重背包可行性-bitset

\(n\) 个数 \(a_i\)。设 \(\sum_{i=1}^na_i=M\),则做 \([0,M]\) 的可行性背包时,可以做到 \(O(\frac{M\sqrt M}{w})\)

相同大小物品分组,至多 \(O(\sqrt M)\) 组,接着二进制拆分,拆分后 \(O(\sqrt M\log n)\) 个物品和仍然是 \(M\),因此可以再次紧缩,重复操作即可。

另一个更优秀的做法是:从小到大在值域上扫描所有数字,若 \(i\) 出现了偶数次 \(c\),则取出两个,同时将 \(2i\) 出现次数加上 \(\frac{c-2}{2}\),如果是奇数次就取出一个,然后给 \(2i\) 加上 \(\frac{c-1}{2}\)

可以证明最终会剩下 \(O(\sqrt M)\) 个数字。

01 背包可行性——单组,极大容量背包

给定 \(n\) 个物品 \(v_i\in [1,D]\),询问是否存在选取方案可以凑出 \(C\)

\(O(nD)\)

考虑方案的组成,答案一定可以组成为 \((l,r)\),也就是 \([1,l-1]\) 的物品全部取了,\([l,r]\) 的物品不一定取了,\([r+1,n]\) 的物品一定没取。

考虑从一个点 \(p\) 开始扩展,设 \(s_p=\sum_{i=1}^pv_i\) 是大于等于 \(C\) 中最小的 \(p\)。则一定满足 \(0\le s_p-C\le D\),从 \([p+1,p]\) 开始扩张,往左转移,存在不改变已经钦定的 \(p-1\) 用了的状态,以及重新钦定 \(p-1\) 没有用,分别的大小变化是 \(0,-v_{p-1}\),往右转移类似的,分别的大小变化是 \(0,v_{p+1}\)

因此左转贡献非负,右转贡献非正,则 \((p+1,p,s_p-C)\),完全可以在第三维与 \(C\) 偏差不超过 \(D\) 的路径上转移到任意可行的 \((l,r,k)\)。在转移路程中,第三维是正数,就去左边找转移,否则去右边找转移。显然覆盖了所有可行路径,且第三维的有效值只有 \([C-D,C+D]\)

考虑优化,可行性太浪费,注意到 \(0\) 的转移,也即 \((l,r,k)\) 可行则 \((l-1,r,k)\) 一定可行。

因此我们设 \(f_{r,k}\) 表示最大的 \(l\) 使得 \((l,r,k)\) 可行。

转移分为:

  1. \(f_{r,k}\leftarrow f_{r-1,k}\)
  2. \(f_{r,k+a_r}\leftarrow f_{r-1,k},k<C\)
  3. \(f_{r,k-a_l}\leftarrow l,l\le f_{r,k},k\ge C\)

第三类转移太烦,但注意到 \((r-1,k-a_l)\to (r,k-a_l)\) 的存在,则只需要对 \(l\in [f_{r-1,k}+1,f_{r,k}]\)\(l\) 进行转移即可。则对于一个 \(k\),总枚举量 $

板题 abc221g

多项式优化背包可行性

P4389

01 背包

选一个随机数组 \(w_i\),相当于算:

\[\prod_{i=1}^n(1+w_iz^{v_i}) \]

的非零项。手算 \(\ln\) 之后用 \(O(n\log n)\) 的调和级数办法求和后 exp 回去。

完全背包

选一个随机数组 \(w_i\),相当于算:

\[\prod_{i=1}^n\sum_{j}w_i^jz^{jv_i}=\prod_{i=1}^n\frac{1}{1-wz^{v_i}}=\exp(-\sum \ln(1-wz^{v_i})) \]

仍然用调和级数求和后 exp。

这里有一个 powered by ddxrS 的小清新倍增

设容量上界 \(m\),设 \(H(z)\) 表示每个数值的存在状况。

考虑先对不大于 \(t=\lfloor\frac{m}{n}\rfloor\) 的数字暴力 dp 求出可行状态 \(F_{\lfloor\frac{m}{n}\rfloor}\)

倍增,有:

\[F_{2^it-1}=F_{2^{i-1}t}\times F_{2^{i-1}t}\times H(z)(\bmod z^{2^{it}}) \]

做完之后给系数非零位置重新赋权防止被卡。

多重背包

选一个随机数组 \(w\),相当于算

\[\prod_{i=1}^n\frac{1-(wz^{v_i})^{c_i+1}}{1-wz^{v_i}}=\exp(\sum \ln(1-w^{c_i+1}z^{v_i(c_i+1)})-\ln(1-wz^{v_i})) \]

还是调和级数求和系数后 exp。

SMAWK 算法

有一个 \(n\times m\) 的矩阵,设 \(L_i\) 为每一行最小值所在的列位置,满足 \(L_i\le L_{i+1}\)

\(O(n+m)\) 次询问矩阵点值得到 \(L\) 序列。

Reduce 操作

\(m>n\) 时,一定是存在列是无用的,reduce 操作通过删除无效列,将 \(m\) 减小到 \(\le n\)

算法原理:有效列中,每一列作为行最小值的位置不会比上一列高。

维护一个 \(k\),初始 \(k=1\)

  • \(a_{k,k}\ge a_{k,k+1}\),说明第 \(k\) 列无用,删去第 \(k\) 列,\(k\leftarrow \max(k-1,1)\)
  • \(a_{k,k}<a_{k,k+1}\)
    • \(k=n\),说明 \(k+1\) 不可能有用,将其删去。
    • \(k<n\),将 \(k\) 加一,纳入考虑范围。
vector<int> Reduce(vector<int>X,vector<int>Y){
	vector<int>nY;int p=0,sur=Y.size(),c=0;
	while(p<Y.size()){
		if(nY.empty()){
			nY.push_back(Y[p]);c=0;++p;
			continue;
		}
		if(ask(X[c],Y[p])<=ask(X[c],nY.back())){
			nY.pop_back();--c;
			continue;
		}
		if(c+1<X.size())++c,nY.push_back(Y[p]);
		++p;
	}
	return nY;
}	

分治策略

首先取出偶数行,递归。

根据 \(L\) 的性质,我们只需要枚举奇数行的答案,总量是 \(O(len)\) 的。

vector<int>SMAWK(vector<int>X,vector<int>Y){
	Y=Reduce(X,Y);if(X.size()==1)return Y;
	vector<int>nX;for(int i=0;i<X.size();i+=2)nX.push_back(X[i]);
	vector<int>g=SMAWK(nX,Y);vector<int>f;
	for(int i=0,lst=0;i<X.size();++i){
		while(Y[lst]<g[i>>1])++lst;
		if(i&1){
			int v=ask(X[i],Y[lst]),p=lst;++lst;
			if(lst<Y.size()&&(i+1==X.size()||Y[lst]<=g[i+1>>1])){
				int k=ask(X[i],Y[lst]);
				if(k<=v)v=k,p=lst;
				++lst;
			}
			lst=p;f.push_back(Y[p]);
		}
		else f.push_back(g[i>>1]);
	}
	return f;
}

复杂度基本线性,懒得分析。

用途—决策单调性优化

对于一个数组 \(f\) 与 一个凸数组 \(g\)\(\max,\min\) 卷积时,有决策单调性,这很显然。

构造矩阵 \(A_{i,j}=f_{i-j}+g_j\)

则卷积完之后的数组相当于查询每一行最小值,应用 SMAWK 即可。

CF1423M。

完全背包最优化——小体积大总量单次询问

ABC221G。

一个很神奇的技巧。

给你 \(n\) 个数字 \(a_i\in [1,D]\),每个数字可以任意用,有其价值 \(v_i\),给你一个 \(C\),问你表达出 \(C\) 的最优方案价值如何。

\(C\) 很大,\(D\) 很小。考虑在 \(O(D^2\log C)\) 解决。

首先暴力 DP 出 \([1,5D]\) 的解,然后考虑递归求解 \(C\)

对于任意足够接近 \(C\) 的方案,其与 \(C\) 偏差不超过 \(D\),因此考虑求出 \([C-D,C+D]\) 的最优方案价值。

分治下去,先算出 \([\frac{C}{2}-D,\frac{C}{2}+D]\) 的最优方案价值,然后暴力卷积得到。

分治到底层可以直接用暴力 DP 的解。

posted @ 2025-08-15 21:12  spdarkle  阅读(14)  评论(0)    收藏  举报