2024.8.16 数据结构专题1
感觉波波今天留的题有点少啊,只给了七道。吃饭还有一个多小时,这会写写总结剩俩紫留晚上研究吧。
A [SNOI2017] 一个简单的询问
这题印象挺深的,之前在交附学莫队之后留的练习题,然后周老师还让我下周去了讲。当时也不太会,研究题解然后写了好久,周内每天晚上写完作业就看一会,有天晚上开始码,然后写到十二点多 还是 \(\text{TLE}\),对着题解改也不知道为啥,最后发现是莫队排序假了,气死。
给你一个长度为 \(N\) 的序列 \(a_i\),\(1\leq i\leq N\),和 \(q\) 组询问,每组询问读入 \(l_1,r_1,l_2,r_2\),需输出
\[\sum\limits_{x=0}^\infty \text{get}(l_1,r_1,x)\times \text{get}(l_2,r_2,x) \]$ \text{get}(l,r,x)$ 表示计算区间 \([l,r]\) 中,数字 \(x\) 出现了多少次。\(N,Q\leq 50000\),\(1\leq a_i\leq N\),\(1\leq l_1\leq r_1\leq N\),\(1\leq l_2\leq r_2\leq N\)。
你会发现它一个询问是由俩不同区间构成的,这很难受,就想到要拆开。
可以得到 \(\text{get}(l,r) = \text{get}(1,r) - \text{get}(1,l-1)\)。那重新设一个 \(f_{i,x} = \text{get}(1,i,x)\),就可以把询问拆成纯用 \(f\) 表示的形式了。
就变成了四部分
答案变成了 \(q_1 - q_2 -q_3+q_4\),把 \(4Q\) 个查询都插到莫队里,累计答案。考虑一下移动端点对答案的贡献:拿 \([1,l]\) 和 \([1,r]\) 说事,比如把 \(r\) 挪成了 \(r+1\),想想是不是 \(x \neq a_{r+1}\) 的答案是不用动的,因为出现次数并没有变化,只需要变一变跟 \(a_{r+1}\) 有关的答案。因为右端次数变多了,那直接给答案加上一个 \(f(l,a_{r+1})\),并且给 \(f(r+1,a_{r+1})\) 的值增加 \(1\) 就可以了。
注意这里 \(l,r\) 的意义,不是区间 \([l,r]\),而是两个区间:\([1,l]\) 和 \([1,r]\)。所以初值 \(l=r=0\),异于常规莫队。
B Bessie's Snow Cow P
这个真的好简单。
一棵 \(n\) 个节点的树,\(q\) 次操作。
1 x c
,将 \(x\) 的子树内都染上 \(c\) 这个颜色。
2 x
,询问 \(x\) 的子树内的颜色丰富度之和。颜色丰富度:每个点的颜色丰富度指它被染过的颜色种类数
可以非常容易的注意到所有的操作都是对子树的。也就是说对于一个点,只要它的祖先染过这个颜色,它也就被染过了。然后既然要求丰富度之和,说明线段树存的就是丰富度之和,考虑怎么算。什么时候能给这个点的子树答案 \(+1\) 呢?是不是它的祖先里从来没染过这个颜色。那你就记录一下哪些点染过这个颜色,判断有没有是他祖先的,如果有就不修改了。如果没有?那再看看有没有是它子树里面的,如果有就扣掉。这里扣掉你可以先给那个染过色的子树 \(-1\),再修改 \(x\) 的子树的时候就能抵消掉。还有点小细节,就是避免算重,你需要把染过色的子树在扣掉之后删除记录,不然会扣重很多小子树。
树剖搞,判断祖先用 \(\text{LCA}\) 就行了,存节点可以用 \(\text{set}\),删除也比较容易。第一发爆 \(\text{RE}\) 了,这使我想到了珂朵莉树的 \(\text{RE}\) 原因,那么就在删除前存一下下一个 iterator
,删掉后转过去就行。
C 春节十二响
一棵有 \(n\) 个节点的树,要求你将一个长度为 \(n\) 的序列划分成若干段,使得任意一段中没有两个数满足它们在树上是 祖先——后代 关系。请你求出每一段最大值之和的最小值。
注意到不能有祖先后代关系,说明 \(x\) 为根的子树内答案需要与 \(fa_x\) 的其他儿子为根的子树内答案合并。子树合并?贪心一下,启发式合并。从大到小排一下子树内答案,再依次合并(取较大值),依次两两合并即可。合并完一个点的所有子树后,往它的所有子节点形成答案序列里加上这个点权,就得到了这个点的答案序列。最后答案便是 \(1\) 号节点的答案序列。
D [SDOI2011] 消防
是当年那个题面很 “潮” 的《树网的核》 的数据加强版。
求树网的最小偏心距(即选一个长度不超过 \(s\) 路径,求其他各个点到这个路径的最大值的最小值), \(n \le 3 \times 10^5\),\(s \le 10^3\)
注意求最小值,那你选的这个路径必须在树的直径上,否则的话直径端点就会拉过来一个特别长的距离使这个最大值较大。
那首先就搞到直径,\(O(n)\)。接着从直径上的每个点开始往外跑,算出一个最远距离(不在直径上) \(dis\),这个 \(dis\) 便会作为答案的最小值,方便之后二分答案,复杂度 \(O(n)\)。那答案范围就是 \(dis\) 到直径长度了,二分答案想想怎么 \(\text{check}\)。
因为我们 \(\text{check}\) 的答案最小值是不在直径上的最大长度,所以我们应该在直径上跑 \(\text{check}\) 验证。我们跑一段 \(ans\),接着跑一段 \(s\),看剩下的距离如果在 \(ans\) 以内,那么就可行,否则不可行。
E [CSP-S2019] 划分
显而易见,想让答案最优,就要让最后一段最小。
由于每一段是单调的,所以单调队列维护一个 \(d_i\) 即以 \(i\) 结尾的这一段的值。
要注意记录每个点的前驱 \(pre_i\) ,最后统计答案,这样才能保证空间不会炸。
F [HNOI2016] 序列
静态离线,莫队显然,线段树在这个题里明显不如 \(ST\) 表,线段树多带两个 \(\log\) 直接起飞。
G [PKUWC2018] Minimax
还没做先不写了。