Loading

一些比较好的 DS 技巧和题目

[Ynoi2015] 即便看不到未来

description

给你一个长度为 \(n\) 的序列 \(a\)\(q\) 次询问,每次询问你一段子区间排序去重后长度分别为 \(1 \sim 10\) 的极长值域连续段个数。

\(1 \le n, V, q \le 10^6\)

solution

考虑离线扫描线处理,按照 \(r\) 右端点排序,对于所有左端点进行处理。

定义 \(lst_i\) 为值 \(i\) 在当前枚举过程中上一次出现的位置,如果当前扫描线枚举到 \(i\),那么按照我们的处理方法,\(lst_{a_i}\) 之前的位置已经没有处理的必要了,因为我们已经有了一个新的 \(a_i\)\(lst_{a_i}\) 这个位置在我们枚举到它的时候已经处理过了。

发现只需要求长度为 \(1 \sim 10\) 的极长值域连续段个数,所以对于当前的值 \(a_i\),可能造成贡献的值只在 \([a_i - 11, a_i + 11]\) 这个范围内,考虑将这个范围内的每个数 \(j\) 求出在 \([lst_{a_i}, i]\) 这个范围内的 \(lst_j\),这将是有用的 \(j\) 最后一次出现的位置。

然后我们从后往前扫,如果扫到了一个有用的值 \(j\),那么只会有三种情况:

  • 接在一个极长值域连续段大的那边。
  • 接在一个极长值域连续段小的那边。
  • 连通左右两个极长值域连续段。

那么我们暴力将每个 \(j\) 左右可扩展的最长值域连续段求出来,然后按照上述三种情况,先把原先的最长值域连续段删掉,然后再将新的最长值域连续段给加上,不难发现,这些新的贡献可以贡献到对应的一段区间上,于是用一棵树状数组维护即可。

[国家集训队] 数颜色 / 维护队列

description

单点修改,区间数颜色。

solution

没有什么好说的,带修莫队第一次写。

考虑将时间轴 \(t\) 带入莫队中,更新的时候我们将修改次数跳到目前询问需要的修改次数即可,注意在修改的时候看一下询问区间是否在修改区间内,然后修改一次后将其与原值取反,因为我下一次做这个更改操作肯定时撤销而不是加入。

[CTSC2018] 混合果汁

[THUPC2017] 天天爱射击

Restore Permutation

description

你有一个排列 \(a\),定义 \(b_i = \sum_{j = 1}^{i - 1} [a_j < a_i] \times a_j\),现在给出 \(b\),要求构造一组 \(a\)

solution

从后往前考虑,我们二分一下目前哪个数可以被选(具体来说,由于我是从后往前填,不用担心互相影响),用树状数组维护即可(树状数组上倍增即可做到单 \(\log\))。

Tree Queries

description

solution

写一个不用脑子的做法。

首先,\(x\) 最远的点肯定是直径中的一个端点,这个很重要,然后我们换根的话,就是分子树内和子树外搞一下 DFS 序,然后不能取到的点只有 \(O(k)\) 段,我们用贪心求区间并把他们合并一下,那么剩下的点的直径的端点之一就是离 \(x\) 最远的点了,同样也是 \(O(k)\) 个区间,相当于我要区间求直径,然后直径合并,这个东西很明显是可以直接线段树处理一下,大力分类讨论即可。

然后我们需要 \(O(1)\)\(LCA\) 做到单 \(log\),具体可以看 Wei 老师的 DFS 序 \(O(1)\) 求 LCA。

[Code+#1] Yazid 的新生舞会

description

solution

这里应该写一个 \(3\)\(\log\) 的分治写法。

「TOCO Round 1」History

description

solution

这里我只想介绍一下如何将结点 \(x\)\(k\) 层对应的 BFS 序左右端点二分出来,具体来说需要利用如下两条性质:

  1. 同一层 BFS 连续且递增。
  2. 一个点的序列 \(s\) 中 BFS 序递增,那么这些点的若干级祖先的 BFS 序一定不降。

第二点很重要,相当于一个连续递增区间的 \(k\) 级祖先都是 \(x\),又由于性质二,于是所有递增 BFS 序的点的 \(k\) 级祖先的 BFS 序都是递增的,于是我们就可以直接二分了。

posted @ 2024-11-20 08:36  Alexande  阅读(40)  评论(0)    收藏  举报