Tag & Difficulty
Sol
难度纯粹个人评价。
3620
Tag & Difficulty
Tag: observation | Difficulty: 2400Sol
codeplus 和 HDU 多校都出现过的原题 /fn 但是线性做法其实还是有点水平的。先枚举绝对众数是谁,把非绝对众数改成 -1,绝对众数改成 1,那么需要计算的就是和大于 0 的区间数量。这容易通过前缀和然后扫一遍做到单次 \(O(n)\) 总复杂度 \(O(n^2)\),但不能接受。
注意到当元素个数很小的时候,前缀和构成的折线倾向于一直往下走,只在其中很少的有当前这个数的一部分有一段小波动。考虑利用这个性质进行优化。
不放假设当前扫到第 \(i\) 个元素,前缀和数组是 \(S\),\(0 \sim i\) 的前缀和最小值是 \(min\)。如果 \(S_{i+1} = min - 1\),那么 \(i+1\) 为右端点一定没有贡献,且紧跟在它后面的所有 \(-1\) 都一定没有贡献。这个时候可以直接不断往后找找到第一个 \(+1\) 的位置从那里开始算贡献。而 \(S_{i+1} \neq min + 1\) 时直接暴力计算贡献。假设这一部分可以做到单次 \(O(1)\)。考虑它的复杂度。假设 \(S_i = min, S_{i+1} \neq min+1\),且 \(j\) 是最靠前的位置满足 \(S_j = min+1\),如果不存在则设它是 \(n+1\)。则 \(i \sim j-1\) 里面至少有 \(\frac{j-i}{2}\) 个 +1,所以这中间暴力扫的复杂度均摊到每个 +1 上就是 \(O(+1\)元素个数\()\) 的,所有的合起来就是 \(O(n)\) 的。
维护上有一个稍微复杂的地方就是如何维护这个前缀和。一个暴力方法是维护一个桶表示每个数的出现次数然后每次加减 1 的时候用一个指针在桶上移动维护增量。直接将这个算法移植到上面这个 idea 上有一个问题:\(S_{i+1} = min - 1\) 的时候后面的一段 -1 需要在桶上打一片标记。这可以用一个前缀和来解决,也可以直接不在桶里维护 \(0 \sim min\) 的第一次 +1,也就是说桶里的所有元素实际上是真实值减 1,这样一段 1 的移动就不会对桶产生影响了。这样就做到了真正的 \(O(n)\)。
3622
Tag & Difficulty
Tag: counting | Difficulty: 2000Sol
容斥,枚举给出的 $m$ 条路径的一个子集强制它们全部同色,用并查集合并一下同色的边集,不同的边集之间没有限制所以方案数就是 $k$ 的同色边集数次方。02.02
3600
Tag & Difficulty
Tag: counting, dp, observation | Difficulty: 2400Sol
先考虑给定一个序列判断它是否合法。考虑删除的若干个区间,它们显然不能相交而不严格包含,而一个区间严格包含另一个区间的时候,小区间显然可以不用删。所以一个序列合法当且仅当可以找到将 $[1,n]$ 恰好划分的若干个区间,使得每个区间的长度均大于等于 2 且首尾相同。这个东西的判断可以考虑这样一个算法:从前往后扫,扫到每个前缀维护一个集合 $S$,表示在当前前缀的最后加 $S$ 中的数字这个序列就合法,否则不合法。同时维护当前这个前缀是否合法。新加入一个数时,如果本身在 $S$ 里则 $S$ 集合不变且整个序列合法;如果加入的数不在 $S$ 里,如果这个前缀是合法的,那么新加入的这个数就会被丢到 $S$ 里,否则不会。最后算到长度为 $n$ 的前缀的时候就可以知道它合不合法了。最后考虑把这个过程放到计数里去。我们可以把序列生成的过程变为从前往后,每次从 \([1,M]\) 里选一个数放在序列最后,那么上面这个算法的过程容易跟这个填数过程同步进行。那么考虑在判断序列合法时我们需要关注什么:需要关注的是当前前缀是否合法,还有 \(S\)。因为每个位置每种数都可以填,所以方案数只跟 \(S\) 的元素个数有关而跟具体值是多少没有关系。这样可以发现状态数是 \(O(n)\) 的,按照上面的算法逐个进行 dp 即可,复杂度 \(O(n^2)\)。
3621
Tag & Difficulty
Tag: binary search, greedy, data structure | Difficulty: 2400Sol
先二分答案,考虑在一个确定的距离 $dis$ 下如何做最优。可以想到一个贪心策略:以任意一个点为根,按照到根的距离从大到小对所有点排序扫描,对于每个点如果距离为 $dis$ 的邻域里没有公园,就在它的距离不超过 $dis$ 的最浅的祖先上放公园。证明这个贪心策略的正确性是简单的:假设当前点距离为 $dis$ 的邻域里没有公园,设 $x$ 是距离不超过 $dis$ 的最浅的祖先,那么 $x$ 一定可以覆盖到 $x$ 子树里所有未覆盖的点(因为当前点深度最大)。对于其他的方案,因为 $x$ 已经是最浅的祖先了,所以其他的方案一定放在了 $x$ 的子树内,这样在 $x$ 子树内和子树外的覆盖能力都没有 $x$ 优,所以 $x$ 就是最优策略。这样使用点分树 + \(O(n \log n)- O(1)\) LCA 维护贪心,可以做到 \(O(n \log V \log n)\),其中 \(V\) 是答案最大值。之前一直在想怎么链分治做,不用全局平衡好像只能 \(n \log^2 n \log V\),感觉这俩玩意还是有本质不同的。
为了代码写起来舒服,在我的代码里找不超过 \(dis\) 的最浅祖先是暴跳的。这么做复杂度实际上是对的:这是因为跳到这个最浅祖先之后,它和它的子树就全都合法了不会再跳,所以每次二分过程中每个点只会被跳到一次。
02.05
好像很多天没更新了
3558
Tag & Difficulty
Tag: constructive, greedy, observation | Difficulty: 2200Sol
3559
Tag & Difficulty
Tag: data structure, tree | Difficulty: 2400Sol
*3560
Tag & Difficulty
Tag: graph, greedy | Difficulty: 3200Sol
!3561
Tag & Difficulty
Tag: dp, greedy, tree | Difficulty: 2600Sol
3563
Tag & Difficulty
Tag: dp, tree | Difficulty: 1900Sol
02.06
3354
Tag & Difficulty
Tag: graph, brute force | Difficulty: 1800Sol
3355
Tag & Difficulty
Tag: tree, data structure | Difficulty: 2100Sol
3356
Tag & Difficulty
Tag: data structure | Difficulty: 2100Sol
!3357
Tag & Difficulty
Tag: tree, dp, data structure / greedy, graph, brute force, complexity analysis | Difficulty: 2600 / 2800Sol
02.10
我是大鸽王 TAT