做题(部分)笔记(From 5.12)
5.12 开始的做题笔记
P5336 [THUSC2016]成绩单
\(f(l,r,x,y)\):把区间 \([l,r]\) 删到不存在值域在 \([x,y]\) 之外的数需要多少代价。\(f(l,r,0,0)\) 代表删到不存在任何数。
讨论决策。决策为:枚举决策点,然后删除右边dp左边,删除左边dp右边,或者dp两边。
要离散化。
https://www.luogu.com.cn/record/50632786
CF1107E Vasya and Binary String
\(f(l,r,k)\):考虑区间 \([l,r]\) 以及 \([l,r]\) 之前的 \(k\) 个 \(s_l\)。这样我们发现 这 \(k\) 个可以和 \(l\) 一起删,或者我们让这 \(k\) 个 \(s_l\) 接在某个断点后面(删除 \([l+1,p-1]\) 后)。
http://codeforces.com/contest/1107/submission/116103030
CF555E Case of Computer Network
有向图强联通 -> 无向图双联通
我们先边双联通分量缩点得到一棵树,我们可以对询问的 \(a,b\) 在路径上标记一个记号。具体就是树上差分,每条边可以得到从上到下以及从下到上的路径数量。如果存在一条边两个都是非0,则就不行。
https://codeforces.com/contest/555/submission/116736738
ARC092F Two Faced Edges
\(u\to v\) 翻转后影响答案当且仅当两个条件【存在 \(v\to u\) 的路径,存在不经过此边的 \(u\to v\)】中有且仅有一个成立。对于第一个,我们可以 \(O(nm)\) 判断两点是否有路径。对于第二个,我们对于 \(u\),遍历每一个出边,然后从这个出边出发给每个点打上一个出边的标记。此后,我们倒序遍历每一个出边,然后从这个出边出发再给每一个点打上一个出边的标记。对于 \(x\),如果两次标记不同那必然代表存在不同的路径;否则必然有相同路径。
https://atcoder.jp/contests/arc092/submissions/22785172
P4323 [JSOI2016]独特的树叶
树哈希换根。首先考虑哈希方案 \(f_u\equiv 1+\sum_v f_vp_{sz_v}\),其中 \(p\) 为质数表。然后我们对其换根。\(g_u\) 表示以 \(u\) 为根提树后的哈希值,有
我们把第一棵树的所有 \(g\) 存进一个 set 中。然后我们对第二棵树做同样的操作。我们枚举其每一个叶子结点 \(u\) 的唯一的出点 \(v\),则删掉 \(u\) 后以 \(v\) 为根的树的哈希值为 \(g_v-p_1\)。我们在 set 中看是否有这个值。如果有的话,那么多余的节点即 \(u\)。
面向数据编程:https://loj.ac/s/1145257
P2272 [ZJOI2007]最大半连通子图
首先可以缩点。然后我们手玩可以发现半连通子图对应缩点后的 DAG 上的链。所以我们相当于找最长链。所以第一问可以直接解决。关于第二问,要注意首先要对 DAG 的边去重(因为是导出子图),然后 DAG 上 DP 也可解决。
https://www.luogu.com.cn/record/51015242
P6186 [NOI Online] 冒泡排序
考虑 \(c_i\) 表示第 \(i\) 个位置前的比它大的个数。那么一次冒泡排序相当于 \(c_i:=\max(0,c_i-1)\)。所以 \(k\) 次冒泡后的逆序对数相当于
所以我们用数据结构维护区间的 \(\sum i\times cnt_i\) 和 \(\sum cnt_i\) 即可。对于交换操作,显然只会最多改变两个数的 \(c\),所以点修即可。Note:线段树遇到0会有奇怪的问题,所以要特判。
https://www.luogu.com.cn/record/51028165
P2590 [ZJOI2008] 树的统计
练了一下树剖板子。好像是最近一年以来写的第一个树剖。ps:看四谎会想让人去弹琴。
https://www.luogu.com.cn/record/51021310
P3313 [SDOI2014]旅行
我们对每一个宗教开一个动态开点线段树(按重儿子优先dfs序)。首先一开始,对于每一个节点最多在其宗教线段树上增加 \(O(\log n)\) 个点,所以一开始有 \(O(n\log n)\) 个点。然后看四个事件。
- 改信,设从 \(c_x\) 变成了 \(c'_x\)。那么我们就在 \(c_x\) 线段树上将 \(x\) 的点设为 0,在 \(c'_x\) 上新加入点 \(x\),\(O(\log n)\)。
- 评级调整。在 \(c_x\) 上修改 \(c_x\),\(O(\log n)\)。
- (和 4. 相同)树剖,并在 \(c_x\) 的线段树上进行查询。\(O(\log^2 n)\)。
总体节点数在 \(3n\log n\),并且时间复杂度 \(O(n\log n^2)\)(\(n,q\) 同阶)。
https://www.luogu.com.cn/record/51090856
P4052 [JSOI2007]文本生成器
考虑补集转换,求出不可读文本数量。在 AC 自动机上跑 DP,设 \(f_{i,j}\) 表示长度为 \(i\),目前在自动机的 \(j\) 上的串数量。我们设一个节点是合法节点,当且仅当自己的 \(fail\) 链无串尾。对于所有节点,儿子中没有的字母的边都可以理解成指向起点。方程 \(f_{i+1,c_{j,k}}\leftarrow f_{i,j}[c_{j,k}\text{ is legal}]\)。
https://www.luogu.com.cn/record/51189117
P3311 [SDOI2014] 数数
考虑和上题一样。\(f_{i,j,k}\) 表示前 \(i\) 大的位,AC 自动机到 \(j\),然后数位 DP 中的 \(edge=k\) 的情况。
注意文本串不能有前导 0,所以在求完 legal 之后把 \(c_{0,0}\) 删掉即可。
https://www.luogu.com.cn/record/51201471
P7514 [省选联考 2021 A/B 卷] 卡牌游戏
首先可以肯定的是会删掉靠按 \(a\) 排序前面的一段和靠后面的一段。然后我们枚举前面删几个,然后再枚举后面删几个,然后查询区间最值即可。
https://www.luogu.com.cn/record/51153465
P7516 [省选联考 2021 A/B 卷] 图函数
一个很nb的东西。首先考虑转化,\(f(u,G)=\) 有多少 \(v\) 满足存在 \(u\leftrightarrow v\) 且路径中只包含 \([v,n]\) 的点。这东西可以随便搞(floyd);然后考虑 \(G\) 的删边问题:对于这个问题,我们通过 floyd DP 的时候记录 \([u,v]\) 所需编号最小的边的集合的编号最大的边即可,然后把这个当做时间戳,做差分。
https://www.luogu.com.cn/record/51158385
P5490 【模板】扫描线
关于懒标记:如果我们线段树的一个节点被直接完整覆盖过了,那么我们直接在上面打一个表示被完整覆盖过的 \(tag\)。然后每次更新的时候,我们查看是否有这个 \(tag\)。如果没有那么就让 \(v=v_l+v_r\)。这样就可以不用懒标记了。
https://www.luogu.com.cn/record/51219144
P1856 [IOI1998]Picture
矩形并的周长。考虑横向边和纵向边。每一次扫描线扫到的横向边长度为上次的总长减去这次的总长的绝对值。纵向边为 \(2\times\) 高度 $\times $ 线段条数。所以我们要多维护一个线段条数即可。
注意,对于两条重合的扫描线,先扫描底边然后再扫描顶边。
https://www.luogu.com.cn/record/51282797
P3320 [SDOI2015]寻宝游戏
考虑树的关键点的 DFS 序 \(d_1,d_2,...,d_n\),则行走的最短路成为 \(dis(d_1,d_2)+dis(d_2,d_3)+\dots +dis(d_n,d_1)\)。所以我们需要有些时候在其中插入一个点和删除一个点,维护 \(d\) 的总和。我们求出 \(x\) 的 DFS 序前驱和后继,设为 \(y,z\),那么答案就加上 \((-1)^{in_x} (dis(x,y)+dis(x,z)-dis(y,z))\)。
https://www.luogu.com.cn/record/51241494
P3322 [SDOI2015]排序
https://www.luogu.com.cn/record/51245511
P1502 窗口的星星
对于 \(x\) 这一个维度我们用队列维护。把所有 \(x\) 在范围内的东西插进一个数据结构。这个数据结构需要求出这所有东西中 \(y\) 之差符合范围的最大区间和。也就是一个一维问题。我们设 \(s_i\) 表示 \(y_{s_i}-y_i\le ...\) 的最大点的值。那么以 \(i\) 作为左端点的窗口坐标值为 \([y_i,y_{s_i}]\)(离散化过后)。我们数据结构中维护对于每一个 \(y\),覆盖了 \(y\) 的区间的权值和。于是现在就是区间更新+最大值查询。
https://www.luogu.com.cn/record/51299314
P2680 [NOIP2015 提高组] 运输计划
考虑我们把路径按照长度 \(l_i\) 进行排序。对于遍历到 \(i\),我们希望 \([i,n]\) 的长度都减小,所以虫洞边一定是路径 \([i,n]\) 这 \(n-i+1\) 条路径的交集中的一条边。我们用点来表示边(父边)。考虑维护一个线段树,对于新加入的一条路景,树剖之后,把那些不在路径上(或者是其 \(lca\))的区间全部清零,然后求全局最大值。设求得的最大值为 \(x\),则 \(ans\) 可以用 \(\max(l_{i-1},l_n-x)\) 来更新。
https://www.luogu.com.cn/record/51417123
P4839 P哥的桶
考虑用线段树套线性基。每一个桶 -> 线性基。线性基的合并是 \(O(log^2)\) 的,所以可以三 log 解决。
https://www.luogu.com.cn/record/51498348
P5459 [BJOI2016]回转寿司
我们把区间和转化为前缀和。所以题目即求有多少 \(s_y-s_x\) 满足 \(0\le x<y\) 且 \(L\le s_y-_x\le R\)。我们转化一下式子,即
然后用随便什么 data structure 求一下即可。
https://www.luogu.com.cn/record/51489274
P5458 [BJOI2016]水晶
我们发现题目中的限制可以转化为:对于除了能量源的点黑白染色(\(B,W\)),如果能量源选择,那么一个能量源周围不能有黑白颜色的点同时被选择。然后考虑建立最小割。\((S,W,c_W),(B,T,c_B)\)。由于能量源的特殊性质(不选择就不会有问题),我们把能量源 \(E\) 拆点。\((W,E,\inf),(E',B,\inf ), (E,E',v_e)\)。
要去重。map 搞一搞即可。
https://www.luogu.com.cn/record/51493838
P2573 [SCOI2012]滑雪
我们会发现这种单步免费回退的可以理解成一棵树,然后最短路径即为其边长和。所以我们相当于要求其最短路径即可。为了保证正确性,终点高度为第一关键字。
https://www.luogu.com.cn/record/51637758
UVA1242 Necklace
等价于 \(S\) 和 \(T\) 在同一个边双内。
https://www.luogu.com.cn/record/51639483
P4254 [JSOI2008]Blue Mary开公司
李超树模板。李超树可以维护多个一次函数,然后查询一个 \(x\) 值上最大的 \(y\)。我们设一个区间的最优势线段为每个区间的中点处的 \(x\) 上最大的 \(y\) 的所在线段。我们在线段树上维护区间最优势线段。对于询问,我们发现,\(x\) 的答案一定是所有线段树上包含 \(x\) 的区间的最优势线段之一。对于修改(插入新线段),我们考虑区间最优势线段 $a $ 和插入线段 \(b\)。分三类讨论。
- 不存在 \(a\) 或者 \(a\) 被 \(b\) 死死压住:那么直接让最优势线段为 \(b\)。
- \(b\) 被 \(a\) 死死压住:直接跑路
- \(a\) 与 \(b\) 在区间的范围内有交:看区间中点哪条线段更优,更新一下答案。然后看斜率,更新左右区间。
李超树要标记永久化。
https://www.luogu.com.cn/record/51705486
P4067 [SDOI2016]储能表
即求出异或大于 \(k\) 的异或和与对数。
可以考虑 trie 上 dp。(实际上好像没那个必要)。
直接数位 DP 即可。\(f(i,0/1,0/1,0/1)\) 表示高->低的第 \(i\) 位,是否到达 \(n-1\),是否到达 \(m-1\),异或是否到达 \(k\)(到达指在上界/下界)。
https://www.luogu.com.cn/record/51708259
P4078 [SDOI2016]探险路线
我们发现最优路线一定是:在起点处徘徊,然后不断横着/竖着S型走->在终点处徘徊。
首先考虑徘徊。我们设 \(f(i,j)\) 表示目前在 \((1,j)\) 且最远走到第 \(i\) 行;\(g(i,j)\) 表示目前在 \((i,1)\) 且最远到第 \(j\) 列。
\(g\) 同理。终点徘徊也同理。
然后考虑横着走(竖着同理)。设 \(h(i,0/1)\) 表示目前在 \((i,1)/(i,m)\) 时的收益。
\(h(i,1)\) 同理。最终合并答案。
https://www.luogu.com.cn/record/51938255
P1712 [NOI2016] 区间
考虑贪心取区间,长度从小到大用尺取法。每次相当于动态更新,看是否存在一个点被覆盖 \(m\) 次(线段树需要求出所有点中被覆盖次数最多的点的个数)。相当于区间加然后全局查。
https://www.luogu.com.cn/record/51982891
P3746 [六省联考2017]组合数问题
考虑思考组合意义。组合意义为从 \(nk\) 个物品中选择 \(ik+r\) 个物品的方案数。设 \(f(i,j)\) 表示 \(i\) 个物品中取 \(\%k=j\) 个的方案数。\(f_{i,j}=f_{i-1,j}+f_{i-1,(j-1)\%k}\)。这可以用矩阵乘法优化。结束。
要注意,k=1的时候转移矩阵中的数为2不是1。
https://www.luogu.com.cn/record/52005437
P2605 [ZJOI2010]基站选址
考虑原始转移方程 \(f(i,j)\) 表示前 \(i\) 个村庄建了 \(j\) 个基站,且第 \(i\) 个村庄一定建基站。
我们设后面那个 \(\sum\) 的内容为 \(v_{k,i}\)。我们发现对于一个建基站的村庄,哪些地方不能被覆盖是不连续的。但是对于一个不建基站的村庄,需要在哪些村庄建基站才能保证自己被覆盖是连续的区间,设其为 \([l_i,r_i]\),这玩意可以用二分解决。
我们把 \(j\) 放在第一层循环。我们考虑维护线段树。然后这个 \(v\) 非常棘手。我们用线段树求出对于 \(i\),\(\min f_{k,j-1}+v_{k,i}\)。考虑对第 \(j\) 层开始建树时,以 \(f_{i,j-1}\) 为初始值,然后考虑 \(i\) 是哪些 \(p\) 的 \(r_p\),然后对于这些 \(p\),我们使线段树上的区间 \([1,l_p-1]\) 全部加上 \(w_p\)(对于 \(i+1\) 以后的村庄,如果要从区间内的点直接转移过来那么就需要赔偿因为无法覆盖)。然后就行了。
一个小技巧。我们在最后多加一个额外的无用的村庄,位置 \(inf\) 基站费用 \(0\) 覆盖距离 \(0\),这样统计答案就方便很多。
草没想到能一遍写对调都没调。
https://www.luogu.com.cn/record/52032417
P6669 [清华集训2016] 组合数问题
考虑真正的卢卡斯定理
即将 \(m,n\) 拆成 \(p\) 进制,则 \(\binom{n}{m}\) 等于各个数位上 \(\binom{n_i}{m_i}\) 的乘积。整除即余数为 0,即存在一个 \(\binom{n_i}{m_i}\) 为 0,即存在一个 \(n_i<m_i\)。所以我们数位DP一下即可。状态的话需要记录是否已经有 \(n<m\),然后 \(n\) 有一个界 \((i\le n)\),\(m\) 有两个界(\(j\le \min(i,m)\),所以边界需要3个状态。