【题解】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 最大值,因为要取模所以其实就是两个滑动窗口。两种处理方式:
- 单调队列。分成两个区间分开处理;
- 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\)。
现在算最大值。
有两个结论:
- 如果我们钦定了一个根,那么她的