【题解】Solution Set - NOIP2024集训Day21 DP常⻅模型2「背包」

【题解】Solution Set - NOIP2024集训Day21 DP常⻅模型2「背包」

https://www.becoder.com.cn/contest/5505


「BZOJ4987」Tree

答案显然是一个连通块,否则不优。

我们在每个连通块深度最小的点统计答案。

树上依赖背包就行。

\(f_{i,j,0/1}\) 从当前根 \(i\) 开始,是否要回到当前根节点。

\(f_{i,j,2}\) 从当前子树任意根节点开始,任意节点结束。

\(2\) 的转移不能写成钦定必须经过 \(i\) 的了:https://www.becoder.com.cn/submission/2594787


「雅礼集训 2018 Day10」贪玩蓝月

插入元素 \(O(p)\) 是好做的。


删除元素的话,如果只有一端,我们直接记录每个版本,然后回退就行。

所以,我们考虑把这个双段队列拆分成两个栈,这样就好处理了。

问题是如果一个栈空了,需要从另一个栈的底部借一个过来,怎么办?

我们直接把非空的那个栈从中间剖开,两边暴力重算,这样时间复杂度是对的。

Why?

考虑势能分析。

定义势能函数 \(h()\) 为两个栈的大小之差的绝对值。

每次加入元素最多会使 \(h()\) 加一。

而每次暴力重构会让 \(h()\)\(0\)。所以时间复杂度是均摊下来就是插入次数 * p。


现在我们 dp 值维护好了,问题在于查询,我们需要合并两个 dp 值。

显然可以 max+ 卷积合并,然后 \(O(p)\) 查询(但是这玩意没有凸性,不能闵可夫斯基和

考虑枚举一个栈内的 dp 值 \(dp_i\),在另一个栈区间查询 \([l-i,r-i]\) 的 dp 最大值,因为要取模所以其实就是两个滑动窗口。两种处理方式:

  1. 单调队列。分成两个区间分开处理;
  2. st 表。这样修改的时候带个 \(\log\)

「SDOI2017」苹果树

如果没有 \(t-h\le k\),那么就是一个裸的树上依赖背包。

一个结论:最深的那个点一定是叶子节点。否则往下走一定更优。

我们不妨去枚举枚举这个叶子节点,现在我们能选取的个数就是 \(k\)。同时整棵树被分成了两个部分,一部分是先序遍历在其前面的,另一部分是后序遍历在其前面的。

我们分别按先/后序遍历,预处理出这个两个多重背包的前缀。

自己的思路大体是对了,但是还有很多细节,具体可以参见 Soluiotn

代码里面的 \(g\) 计算的范围是先序遍历在 \(i\) 前面和 \(i\) 这条链上的点。\(f\) 是后序遍历在 \(i\) 前面的点。

关于单调队列优化多重背包。

(我不会所以来口胡一下。

先把朴素的 \(O(m\sum cnt_i)\) 转移拿出来:

\[f_{i,j}=\max_{k=0}^{cnt_i}\{f_{i-1,j-k\times w_i}+k\times v_i\} \]

但是这道题 \(w_i=1\) 我们可以很聪明的把式子改写成:

\[f_{i,j}=\max_{k=0}^{cnt_i}\{f_{i-1,j-k}-(j-k)\times v_i\}+j\times v_i \]

这样就变成真正的单调队列的板板了。如果 \(w_i\) 任意的话,\(j-k\times w_i\) 不再连续,所以就不能用这种比较巧妙的东西,得改循环顺序,具体参见 OI-Wiki

「BZOJ3425 Poi2013」Polarization

由于每条边至少贡献一次(就这条边两旁的两个点),同时我们可以每层让边反向构造出这组方案。

所以第一问最小值就是边数 \(n-1\)


现在算最大值。

有两个结论:

  1. 如果我们钦定了一个根,那么她的
posted @ 2024-09-03 08:09  CloudWings  阅读(23)  评论(0编辑  收藏  举报