护照
在第 \(i\) 个点买一张票,就能在 \([L_i,R_i]\) 中任意行走,求从每个点出发,最少买几张票能走遍 \([1,n]\)?
tag:最短路,线段树优化建图。
题目的问题是求最少代价,于是我们发现题目很像一个最短路模型:\(i\) 向一个虚点 \(u_i\) 连边权为 \(1\) 的边,\(u_i\) 向 \([L_i,R_i]\) 连代价为 \(0\) 的边。连边看起来很多,但是由于连边连向的是一个区间,我们只需要线段树优化建图即可。
考虑最终的答案是什么:由于连边是连向一个区间,所以对于点 \(i\),所有买的票的并,也就是我们能走到的点,构成了一个大区间。所以走遍 \([1,i]\) 的充要条件是走到 \(1\) 。因为 \(1\) 为该区间左端点。同理走遍 \([i,n]\) 的充要条件是走到 \(n\),因为 \(n\) 为该区间右端点。所以我们建反图并从 \(1\) 与 \(n\) 分别跑最短路,每一个点的答案应该是到 \(1\) 的最短路加上到 \(n\) 的最短路。
然后我们发现这个不对,因为到 \(1\) 的最短路和到 \(n\) 的最短路会有重复的路径,这一段路径只会被计算一次。例如,从点 \(i\) 出发只需要买一张 \(i\) 的票就可以了。
我们如果设初始 \(dis_1+dis_n\) 为初始答案 \(ans_u\),那么我们发现对于任意的 \((u,v,w)\),\(ans_v \le ans_u + w\)。于是我们考虑再次使用最短路,只不过这一次往优先队列中加入所有点作为源点进行松弛。
如果对算法进行思考,我们会发现只需要将所有票的虚点作为源点即可。原因如下:
- 对于线段树上的点,它们只负责联通原始图中有边的点,从其出发的边权值都为 \(0\),因此不用松弛。
- 对于原有点,我们建的反图中必定有票的虚点连向原有点的边,因此只要不是无解情况,原有点必定会被松弛到。
Election
由
c,t
组成的字符串,每次询问一个区间,问至少需要删掉多少个t
,才能使在子区间任意一段前后缀里,c
的个数都不小于t
的个数。
tag:线段树
两类字符比大小,很经典的操作就是把一个视为 \(-1\),另一个视为 \(1\)。这里我们把 c
视为 \(-1\),把 t
视为 \(1\),那么原问题转化为:需要删掉多少个 \(1\),才能使子区间任意一段前后缀和都不大于 \(0\)。
考虑先满足前缀,再满足后缀。我们贪心地删除:如果这一个 \(1\) 会使前缀大于 \(0\),我们才将它删除。这不仅用最少的次数满足了前缀都不大于 \(0\) 的限制,还将删除的点尽量右移。这会使后缀的修改更优。
考虑用变量描述前缀需要的最大删除数。我们设变量 \(pre_i\) 表示 \(l\) 到 \(i\) 的前缀和,可以发现当我们修改了 \(pre_i\) 变为 \(0\) 时,之后我们第一个修改的 \(pre_j\) 必定大于 \(pre_i\),且修改的次数变为 \(pre_j-pre_i\)。那么只满足前缀的答案是 \(\max\{pre_i\}\)。
现在考虑后缀。后缀的难点在于我们已经提前删除了一部分 \(1\) 了。于是我们考虑删除了多少。对于点 \(i\),之前在满足前缀过程中删除的点为 \(\max\{pre_j\}(j\le i)\),那么之后删除的点为 \(\max\{pre_k\}-\max\{pre_j\}(j\le i)\)。所以若设原序列 \(r\) 到 \(i\) 后缀和为 \(suf_i\),新序列后缀和为 \(new_i\),那么 \(new_i = suf_i - (\max\{pre_k\}-\max\{pre_j\}(j< i))\)。
考虑最终答案 \(\max\{pre_i\} + \max\{new_j\}\),把脑子放进垃圾桶,我们来推式子:
\[\begin{aligned} \max\{pre_i\} + \max\{new_j\} = \max\{pre_i\} + \max\{suf_j - (\max\{pre_k\}-\max\{pre_i\}(i< j))\} \\= \max\{pre_i\} - \max\{pre_k\} + \max\{suf_j +\max\{pre_i\}(i< j)\} \\= \max\{pre_i\ + max\{suf_j\}(i< j)\} \end{aligned} \]发现这实际上就是前缀的最大值加上在它右边的后缀的最大值。用线段树维护即可。
y-fast trie
给出常数 \(C\) 和一个初始为空的集合 \(S\),你需要支持以下操作:
- 增加一个数 \(x\),保证 \(x\) 在 \(S\) 中没有出现过。
- 删除一个数 \(x\),保证 \(x\in S\)。
在每次操作后,你需要输出 \(\sum_{a,b\in S}(a + b) \bmod C\)。强制在线。
\(n \le 5e5\)
tag:模型刻画。
首先不难发现取模实际上把答案分成了两种,\(a+b-C\) 和 \(a+b\)。
于是把 \(a+b\) 分类。分成 \(a+b\ge C\) 和 \(a+b < C\)。对于前者,我们找出 \(S\) 内的最大的两个数匹配即可。下文着重考虑 \(a+b\) 的情况。
对于 \(a\),发现 \(a+b\) 随着 \(b\) 的增加而增加,直到 \(a+b \ge C\) 为止。所以 \(a\) 会选择其中最大的 \(b\)。一种暴力的想法是,我们对于每个 \(a\),找出使 \(a+b<C\) 的 \(b\) 的最大值。时间复杂度 \(O(qC\log C)\),具体取决于集合内数的个数。
考虑优化这个暴力。对于每个 \(b\),发现 \(a+b\) 随着 \(a\) 的增加而增加,直到 \(a+b \ge C\) 为止。所以 \(b\) 会选择其中最大的 \(a\)。于是 \(a\) 和 \(b\) 就从单项选择变为了双向选择。这是很好的,因为这就意味着所有 \(a\) 和 \(b\) 两两匹配。
考虑维护匹配。对于增加操作,我们找到其作为 \(a\) 能匹配到的最大的 \(b\),如果 \(b\) 没有匹配则可以直接匹配。否则若 \(a\) 比与 \(b\) 匹配的点更优,我们就扔掉 \(b\) 匹配的点,并将其与 \(a\) 匹配。如果匹配更新,则也要与当前答案比较。
对于删除操作,若没有匹配,可以直接删除。若有匹配,我们需要在删除 \(a\) 后另外为 \(b\) 寻找匹配。这是增加操作做的事情。因此我们删去两个数,并再次增加 \(b\) 即可。这些操作都可以使用 set
完成,时间复杂度 \(O(q\log C)\)。
意外地难写。
标签:pre,匹配,删除,后缀,max,8.23,我们 From: https://www.cnblogs.com/closureshop/p/17652813.html