杭州集训 Day 3

课前

早饭 htdlz 帮忙买的,一碗粥和三个不知名的糕点,粥并不好喝,但是糕点好吃。

早上到了机房把这儿的小破电脑换成了自己的笔记本,屏幕大一点舒服一些。hs_black 走了,今天换 syksykccc 来讲,syk 开朗幽默的多,上课和机房这群很有话题。而且他甚至把他讲的每个题对应的代码打了,然后课后发了。

课前他自我介绍说:“我是高一拿的 Ag,后来觉的 OI 比 whk 难然后就 AFO 了,最后裸分考的北大。”

syk 还发表了一些逆天言论,如 “破链为环” 解决区间 dp 问题

笔记

P2051

\(f[i][j][k]\) 表示从左上角一直到坐标 \((i,j)\) 并且用了 \(1\)~\(k\) 个象棋的方案数

\(n\) 的数据范围很小,可以 \(n^3\) 暴力遍历

\(v[i][j]\) 标记棋子

但是转移并不好写,考虑找性质,发现对于每行每列炮的个数不会超过两个

所以将第二,三维优化

\(j\) 表示 有 \(j\) 个列是 \(1\) 个炮,\(k\) 表示有 \(k\) 个列放 \(2\) 个炮,剩下的放 \(0\) 个炮

转移的时候就考虑在当前行放 \(0 / 1 / 2\) 个炮,以及这些炮放在了之前有几个炮的列上

现在可以暴力转移,复杂度三次方级,可以通过

P1437

将原图形旋转变成左下角为直角的等腰直角三角形形式,那么想要拿走一个砖,就必须要拿走左边那个和左上角那个,考虑有依赖的背包 dp

显然的,可以将当前砖和左边那个和左上角那个绑定,他们的分值和为质量,个数为体积,然后背包

但是一定会有重复绑定导致计算错误,所以要考虑找特殊性质,发现第 \(i\) 行被打掉的一定是一个前缀,用 \(f(i, j, k)\) 表示当前考虑到第 \(i\) 行,这一行的轮廓线转折点在 \(j\),已经打了 \(k\) 个砖头的最大收益。

\[f(i,j,k) = \max_{l≥j-1} \{ f(i-1,k,k-j)+S_i(j) \} \]

复杂度 \(O(n^4)\)

P6189

显然可以平方级别的背包 dp,但是显然过不了。但是对于背包 dp 它也有两种不同的状态设计,

\(f(i, j)\) 表示把 \(i\) 拆成 \(≤j\) 的数的方案。\(g(i, j)\) 表示把 \(i\) 拆成 \(j\) 个数的方案。这个直接递推转移是非常好实现的。

之后根号分治,对于小于等于 \(\sqrt n\) 的数据计算 \(f\),剩下的计算 \(g\)(这里数据指的是 dp 第二维)

复杂度 \(O(n \sqrt n)\)

之后考虑如何使用 \(f,g\) 更新计算答案

\[\sum_{s=0}^n (f(s, M) \times \sum_{c=0}^Mg(n-s,c)) \]

\(M\) 指的是根号分界

P7962

将答案式子展开化简后为 \(n \sum_{i=1}^na_i^2 - (\sum_{i=1}^na_i)^2\)

对于一次操作,原来相邻两个数的差为 \(a_i - a_{i-1}\) 变为了 \(a_{i+1} - a_i\)。即一次操作是在交换差分数组的相邻两项,
可以任意交换差分数组得到一个新的差分排列。为了使所有数字趋于平均值,差分数组应当是单谷序列,微扰法可以证明。将差分数组 \(b\) 从小到大排序,然后依次考虑每一项,要么插在当前最左边,要么插在当前最右边。

\(a_0 = 0\),设 \(f(i,s)\) 表示考虑了前 \(i\) 个数,当前 \(∑a_i = s\)
的情况下,最小的 \(\sum a_i^2\) 是多少。

  • \(f(i,s+\sum_{j=1}^{i}b_j) = f(i-1,s)+(\sum_{j=1}^{i}b_j)^2\),放在右边

  • \(f(i,b_i*i+s)=f(i-1,s)+i*b_i^2+2*b_i*s\) ,放在右边

直接 dp 复杂度为 \(O(n^2a)\)

注意到差分数组中非 \(0\) 项至多 \(O(a)\) 个,而对于 \(b_i = 0\) 毕竟排在最前面,无论放左放右都是一样的,所以直接铺开就行了,于是时间复杂度就能做到 \(O(na^2)\)

P6764

计算出 \(P_i = 0/1\) 表示能否从 \(i\) 开始进行一次粉刷,之后的就是很平凡的贪心区间覆盖问题。

假设现在 \(|F_c| ≤ 1\),那么每个墙能指派的承包商是固定的。用 \(f(i)\) 表示从第 \(i\) 段墙开始最远能刷到哪里。

\(f(i, j)\) 表示从第 \(i\) 段墙开始,让承包商 \(j\) 来粉刷,最远能刷到哪里。可以 \(O(nm)\) 逆序递推。初始化 \(0xcf\),对于 \(j ∈ F_{c_i} ∧ (i = N − 1 ∨ (j + 1) \mod m ∉ F_{c_i}+1\) 的情况,\(f(i,j) = i\)。剩下的 \(j ∈ F_{c_i}\) 的情况 \(f(i + 1,(j + 1) \mod m)\)

然后还有一些优化的 trick 卡一下就过了

P5289

\(f(x, y)\) 这样的状态去做背包,表示考虑到当前学校的时候,红阵营有 \(x\) 人,\(A\) 派系有 \(y\) 个人的方案数。复杂度是 \(O(nM^2)\)

假如 \(k = 0\) 发现可以先计算阵营的分配方案数,再计算派系的方案数,然后把两个乘起来即可。这两个都是可以一维背包实现的,复杂度 \(O(nM+cM)\)

对没有限制的学校用分别 \(dp\) 的方式,对于有限制的学校,因为不超过 \(k\) 个,所以用二维 \(dp\) 的方式再合并一下答案,总复杂度 \(O((n + c)M+ k^2sM)\)

P4302

\(f(l,r)\) 表示 \(S[l · · ·r]\) 的答案。

  • 第一条:\(f(l,r) ← (r − l + 1)\)

  • 第三条:\(f(l,r) ← f(l, k) + f(k + 1,r)\)

  • 第二条:枚举循环节长度 \(k\)
    \(f(l,r) ← f(l, l + k − 1) + 2 + digits((r-l+1)/k)\)

枚举 \(k\)\(r − l + 1\) 的因子的去检查,调和级数可得复杂度 \(O(n^3 \log n)\)

P6879

时间值域太大了,不好设计进状态,但是答案很小,考虑交换定义域和值域设计 dp 状态。

\(f(l,r, b_{lr}, k)\) 表示当前考虑了 \([l,r]\) 这一段的所有打卡点,
\(b_{lr}\) 表示终止时站在 \(l\) 还是 \(r\),真正成功打卡了 \(k\) 个点时,最小花费的时间。用每一个已经求出的 \(f(l,r, b_{lr}, k)\) 的状态去更新 \(f(l − 1,r, *, *)\)\(f(l,r + 1, *, *)\)\(k\) 是否要增加可以根据两个 \(b\) 的值很方便地计算。

那么初始时把所有的状态都赋值为无穷,如果一个状态的最小时间不是无穷就说明有意义,用它去刷表。最后要查看有意义的状态的 \(k\) 的最大值。

复杂度 \(O(n^3)\)

P1864

不能修改数据值,所以这棵树的中序遍历固定。另一点是,由于可以修改为任意实数,实数是可以无限接近但是不相等的,所以完全可以理解为,允许权重相同,并且权重相同的点可以以任何你想要的顺序排序。即允许假定权重是整数来考虑这个问题。用 \(f(l,r, k)\) 表示把 \([l,r]\) 建树并且保证子树内所有点的权重都 \(≥ k\) 的时候,最小化的答案。\(f(l,r, k)\) 通过枚举根节点 \(t\),分别考虑不需要 /需要修改,利用下两式来更新

\[f(l, t − 1, w_t) + f(t + 1,r, w_t) +∑_{i=1}^r freq_i (w_t ≥ k) \]

\[f(l,t-1,k) + f(t+1,r,k)+K+\sum_{i=1}^r freq_i \]

其中 \(∑freq_i\) 表示深度 \(+1\) 之后代价的增量。总复杂度为 \(O(n^4)\)

P7605

\(f(l,r)\) 为答案

转移 \(f(l,r)\) 考虑第一步选择了左边还是右边,不妨设左边。

如果最终都没消去,那么就是 \(f(l + 1,r) + (1, 1)\)

如果最终利用 \(c_k\) 消去了,考虑一下 \(c_k\) 是从左端取出的还是
从右端取出的。

不妨设是右端取出的,那么显然 \([k + 1,r]\) 要先被删除,考虑
枚举 \(j\) 表示 \([l + 1, j]\) 辅助了这一段的删除,那么转化后的子
区间就是 \([j + 1, k − 1]\)

\(\max\{f(j + 1, k − 1),2, 1 + g(l + 1, j, k + 1,r)\}\) 来表示这个转移

其中 \(g(l_1,r_1, l_2,r_2)\) 表示完全消除 \([l_1,r_1] ∪ [l_2,r_2]\) 所需要的栈的大小。当然也有可能为无穷,此时对应的状态不能用来转移

关于 \(g\) 的转移,考虑是先取出 \(l_1\) 还是 \(r_2\)

比如是 \(l_1\),考虑它和哪个位置配对,比如是和 \(c_i\),其中 \(i ∈ [l_2,r_2]\)。再枚举左侧辅助消除 \([i + 1,r_2]\) 的区间是 \([l_1 + 1, j]\),那么转移就可以表示为 \(\max\{g(l_1 + 1, j, i + 1, r_2) + 1, g(j + 1,r_1, l_2, i-1)\}\)

理论复杂度 \(O(n^6)\) ,实际上可以快速判断一些 \(g\) 的值为无穷,比如区间长度和是否为偶数,区间内的数是否两两配对(可以哈希之后求异或
和)。这样常数很小。可以用记忆化搜索实现。

P7077

如果没有第 \(2\) 类,考虑计算每个第 \(1\) 类函数的实际被调用次数。

\(2\) 类函数可以看作,对原始数据的乘法 + 将之前的第 \(1\) 类函数调用 \(V_i\) 次。

建立一个 \(DAG\),每个第 \(3\) 类函数(可以把 \(f_1, · · · , f_Q\) 也建模成一个第 \(3\) 类函数)向它所调用的所有函数依次连边。

首先考虑计算原始数据的贡献,这个只需要一次 dp 求出每个函数调用造成的全局乘积大小,然后就可以方便的计算总倍率。

逆序第 \(3\) 类函数序列。动态维护一个乘积 \(prod\) 表示当前子函数需要执行多少次。每遇到一个子函数,就在它对应的节点上打 \(+prod\) 标记,然后再更新 \(prod\)。这样便可以拓扑排序后利用 dp 求出每个函数需要调用的次数,累加贡献。

复杂度 \(O(n + \sum C_i)\)

P4577

每个节点开一个 multiset \(f_u\),从大到小的第 \(x\) 个数,表示当答案为 \(x\) 时的最大转移权值。

首先不考虑 \(u\) 的话,\(f_u =∪f_v\)

考虑 \(u\),那么把 \(w_u\) 插入 \(f_u\),然后除非 \(w_u\)\(f_u\) 中最小的,否则把 \(w_u\) 的前一个 iterator 删除。合并 \(f\) 时可以使用启发式合并,复杂度 \(O(n \log^2 n)\),如果使用权值线段树维护可以做到单 \(\log\)

P5024

\(f(u, 0/1)\) 表示 \(u\) 的子树,\(u\) 不选 / 选,从“转移模式”来
看,除了 \(a, b\),其他的点依然在做相同的操作。

首先一次 \(dp\) 求出前文提到的 \(f(u, 0/1)\) 数组,现在考虑把 \(f\) 倍增化。用 \(g(u, k, 0/1, 0/1)\) 表示对于 \(u\),它选不选,它的 \(2^k\) 级父亲 \(fa\) 选不选,则 \(tree(fa) − tree(u)\) 的最优代价。初始化为

\[g(u, 0, 0, 0) = inf \]

\[g(u, 0, 1, 0) = f(fa, 0) − f(u, 1) \]

\[g(u, 0, 0/1, 1) = f(fa, 1) − min(f(u, 0), f(u, 1)) \]

倍增转移时枚举 \(2^{k−1}\) 级父亲的状态

现在分两种情况讨论询问。如果 \(a, b\) 是祖先-后代关系,那么,先用 \(b\)\(f\) 去初始化状态 \(res_0,res_1\),分别表示当前的根节点是不选/选的最小代价。然后用 \(g\) 去倍增到 \(a\),再把 \(res_x\) 赋值为 \(0\),最后一路倍增到根节点即可。如果不是的话,同理,分别从 \(a, b\) 用上面的操作倍增到 LCA 下方的节点,然后在 LCA 处暴力合并答案,再倍增到根节点即可。复杂度只有一个 \(\log\)

课后

绝区零常驻池第一个常五是狼哥,第二个十连出了苍角,抽那个小不点不知道是什么的东西上来十连出了一个金,后来换成鲨什么蓝色的那只十连又出了。

暗示我抽鲨鱼妹

但是我 50 抽了还没出,运气太差

傻鸟米哈游不如洛谷好玩

posted @ 2024-10-23 09:08  PassName  阅读(6)  评论(0编辑  收藏  举报