P9140 [THUPC 2023 初赛] 背包 做题记录
P9140 [THUPC 2023 初赛] 背包 做题记录
Descreption
有 \(n\) 种物品,第 \(i\) 种物品单个体积为 \(v_i\)、价值为 \(c_i\)。
\(q\) 次询问,每次给出背包的容积 \(V\),你需要选择若干个物品,每种物品可以选择任意多个(也可以不选),在选出物品的体积的和恰好为 \(V\) 的前提下最大化选出物品的价值的和。你需要给出这个最大的价值和,或报告不存在体积和恰好为 \(V\) 的方案。
\(1 \le n \le 50, 1 \le v_i \le 10^5, 1 \le c_i \le 10^6, 1 \le q \le 10^5, 10^{11} \le V \le 10^{12}\)。
Solution
注意到每次询问的 \(V\) 非常大。
那么对于这种极大容量的背包问题,我们可以加入一点贪心:尽可能选择 \(\dfrac{c_i}{v_i}\) 最大的物品。
但我们会受到 恰好 选出体积为 \(V\) 的限制,单纯的贪心显然不正确。
不妨以 \(v_i\) 作为模数,考虑同余最短路。设基准物品体积为 \(P\),价值为 \(W\)。
假设我们有一种选择物品的方案,选出的物品体积之和为 \(V'\),价值之和为 \(C'\),且 \(V' \equiv V \pmod{P}\)。我们把剩下的背包全部用基准物品填满,答案为 \(C'+\dfrac{V-V'}{P} \times W=C'+\lfloor \dfrac{V}{P}\rfloor W-\lfloor \dfrac{V'}{P}\rfloor W\)。
我们需要最大化 \(C'-\lfloor \dfrac{V'}{P} \rfloor W\)。那么我们设 \(f_x\) 为 \(V' \equiv x \pmod{P}\) 时,\(C'-\lfloor \dfrac{V'}{P} \rfloor W\) 的最大值。
建边:\(f_x \rightarrow f_{(x+v_i)\bmod P}\),边权为 \(c_i-\lfloor \dfrac{x+v_i}{P} \rfloor W\) 。
注意到这个图不会存在正权环。如果存在正权环,意味着存在一种选出物品体积之和为 \(kP\) 的方案,其价值比直接选择 \(k\) 个基准物品收益更大。这与 \(\dfrac{W}{P}\) 的极大性矛盾。
对于每个 \(f_x\),我们最多选择 \(P\) 个物品,\(V'\) 的最大值 \(P \times \max(v_i)\leq 10^{10} \leq V\),保证了正确性。
在实现中,我们无需真的建出图来。回归本质,同余最短路的本质还是 dp。对于一个体积为 \(v_i\) 的物品,在长度为 \(P\) 的环上会形成 \(\gcd(v_i,P)\) 个子环,每个环大小为 \(\dfrac{P}{\gcd(v_i,P)}\)。我们在每个环上转 \(2\) 圈,就可以覆盖所有的转移。
int n;
ll v[N],c[N],Q,W,P,f[M];
signed main(){
//效率!效率!
read(n),read(Q);
P=1,W=0;
for(int i=1;i<=n;i++){
read(v[i]),read(c[i]);
if(c[i]*P>v[i]*W)
P=v[i],W=c[i];
}
memset(f,-0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
int d=__gcd(P,v[i]);
for(int j=0;j<d;j++){
for(int x=j,t=0;t<=1;t+=(x==j)){
int y=(x+v[i])%P;
Ckmax(f[y],f[x]+c[i]-((x+v[i])/P)*W);
x=y;
}
}
}
while(Q--){
ll V; read(V);
ll r=V%P;
ll ans=f[r]+V/P*W;
if(ans<0) puts("-1");
else printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号