UOJ 比赛题

#388. 【UNR #3】配对树

先考虑怎样配对最优,发现对于一条边,若其两端的子树内需要配对的点的个数都为奇数,则该边会有 \(1\) 的贡献,否则没有贡献,得这样为最优情况。

那么对于一棵子树,将其内部的点标记为 \(1\),得到一个 \(01\) 串,子树父边的贡献为 \(01\) 串中长度为偶数且区间和为奇数的区间个数。

可以用线段树合并来优化,线段树上每个节点维护 \(v(cur,0/1,0/1)\),表示 \(cur\) 对应的区间中前缀和为偶数或奇数,位置为偶数或奇数的位置个数,设点 \(x\) 的父边权值为 \(val\),得其对答案的贡献为:

\[\large val(v(rt_x,0,0)v(rt_x,1,0)+v(rt_x,0,1)v(rt_x,1,1)) \]

为方便计算位置个数,线段树区间设为 \([1,m+1]\)

提交记录

#21. 【UR #1】缩进优化

枚举缩进长度,得答案为:

\[\large\begin{aligned} &\min_{x}\left ( \sum_{i=1}^n \left \lfloor \frac{a_i}{x} \right \rfloor + a_i \bmod x \right ) \\ =&\min_{x}\left ( \sum_{i=1}^n a_i +(1-x)\sum_{i=1}^n \left \lfloor \frac{a_i}{x} \right \rfloor \right ) \end{aligned} \]

那么只需考虑如何计算 \(\sum\limits_{i=1}^n \left \lfloor \frac{a_i}{x} \right \rfloor\),直观的想法根号分块差分处理 \(a_i\) 对每个 \(x\) 的贡献,但这样复杂度为 \(O(n \sqrt n)\),过不去。

记录每个数的出现次数,然后枚举 \(\left \lfloor \frac{a_i}{x} \right \rfloor\),查询区间 \(\left [ \left \lfloor \frac{a_i}{x} \right \rfloor x,(\left \lfloor \frac{a_i}{x} \right \rfloor+1)x-1 \right]\) 的出现次数,复杂度为 \(O(n \log n)\)

提交记录

#119. 【UR #8】决战圆锥曲线

\(c=0\) 时,是直线找切点,用线段树维护区间的上凸壳即可,因为数据随机,所以凸包大小为 \(O(\log n)\),修改就暴力合并凸包,询问就暴力扫凸包,翻转就将下凸壳变成上凸壳,复杂度为 \(O(m \log^2 n)\)

\(c \ne 0\) 时,是一个双曲线的右支找切点,那么第 \(i\) 个点要对答案有贡献的条件是 \(\forall j>i,y_j<y_i\),得其在随机数据下有贡献的概率为 \(\frac{1}{n-i+1}\),因此有贡献的点数为 \(O(\log n)\),用线段树上维护有贡献的点,复杂度为 \(O(m \log^2 n)\)

考虑只在线段树上维护区间最值,设当前递归到的区间为 \([l,r]\),当前得到的最优答案为 \(ans\),先考虑右儿子,再考虑左儿子,若点 \((r,\max_{rs})\) 的值大于 \(ans\),才进入右儿子,若点 \((mid,\max_{ls})\) 的值大于 \(ans\),才进入左儿子。

若进入左儿子,则说明左儿子内可能存在一点比右儿子内的所有点都优,因为可能成为答案的点个数为 \(O(\log n)\),所以在线段树上询问时的分叉次数也为 \(O(\log n)\),得一次询问复杂度为 \(O(\log^2 n)\),其他操作复杂度为 \(O(\log n)\)

提交记录

#177. 新年的腮雷

二分最后的答案,发现正着合并元素不好处理,考虑倒着拆分元素。

设二分的值为 \(val\),若最后 \(val\) 拆分后得到的元素和 \(a_i\) 匹配后,都大于等于对应的 \(a_i\),则判定二分合法,这里的匹配即为从小到大对应。考虑维护两个集合 \(S,T\),一开始时,\(S\) 只有 \(val\)\(T\) 中为所有 \(a_i\),然后分类讨论。

\(S\) 中最大值 \(s\) 减去 \(b_i\) 的最小值小于 \(T\) 中的最大值 \(t\),则在 \(S\) 中找到最小的满足大于等于 \(t\) 的元素,将其和 \(t\) 一起删去,找不到或 \(|S|=0\) 时,则判定不合法。若大于等于 \(t\),则删去 \(s\),向 \(S\) 中加入 \(\{ s-b_1,s-b_2,\dots,s-b_m \}\)

考虑正确性,当小于 \(t\) 时,说明 \(S\) 中任意一个元素拆分后都小于 \(t\),所以 \(t\) 必须和当前 \(S\) 中的一个元素匹配。当大于等于 \(t\) 时,发现此时 \(s\) 不进行拆分一定比之后拆分不优。

\(multiset\) 维护集合即可。

提交记录

#218. 【UNR #1】火车管理

可以在线段树上用平衡树或者堆维护不下传的标记,弹栈就是查询一路上经过的节点上时间最晚的标记,但复杂度为 \(O(n \log^2 n)\)

考虑用可持久化线段树维护每个时刻栈顶的信息,询问和压栈就直接在线段树上操作,弹栈时先查询栈顶下一个元素的信息,然后更新栈顶。

提交记录

#187. 【UR #13】Ernd

当满足 \(b_i-b_{i-1}\geqslant |a_i-a_{i-1}|\) 时,才能使第 \(i-1\) 个和第 \(i\) 个水果都被接到。

\((b_i,a_i)\) 看作平面上的点,坐标系进行变换得 \((b_i, a_i) \Rightarrow \left( b_i - a_i, b_i + a_i \right)\),那么当 \(x_j\leqslant x_i \and y_j\leqslant y_i\) 时,第 \(j\) 个和第 \(i\) 个水果就都能被接到。

考虑 \(dp\),设 \(f_i\) 为最后接到的水果为 \(i\) 的最大得分。

\(x_j\leqslant x_i \and y_j\leqslant y_i\) 时,有:

\[\large f_i = \max(f_i,f_j+1) \]

\(\forall k,j\leqslant k<i,x_k\leqslant x_i \and y_k\leqslant y_i\) 时,有:

\[\large f_i=\max(f_i,f_j-1+(j-i+1)^2) \]

排序后用树状数组维护二维排序即可完成第一种转移,发现排序后不影响第二种转移中连续段内元素的相对顺序,所以转移顺序不会受影响,对每个连续段开一个栈维护上凸壳斜率优化即可。

提交记录

#84. 【UR #7】水题走四方

设两个人分别为 \(A,B\),考虑可以将任意一种方案转化为只有 \(B\) 在瞬移,而 \(A\) 从来没瞬移过。那么整个过程就是 \(A\) 走一条从根到叶子的路径,\(B\) 不断瞬移到 \(A\)。称 \(B\) 瞬移到 \(A\) 时,\(A\) 的位置为关键点,这里将根节点和最后到达的叶子节点也看作关键点。若已知关键点是哪些,就对应了一种方案。

考虑 \(DP\),设 \(f_x\)\(x\) 作为路径上最后一个关键点的最小花费,\(sum_x\)\(x\) 子树内所有叶子的深度和,\(cnt_x\)\(x\) 子树内叶子个数,得:

\[\large f_x=\min_{y\in anc(x)}\left\{ f_y+sum_y-sum_x-(cnt_y-cnt_x)dep_y+\max\{dep_x-maxdeep,0\} \right\} \]

其中 \(maxdeep\)\(subtree(y) \setminus subtree(x)\) 中深度最深的叶子的深度,因为 \(A\) 可以在 \(B\) 去最后一个叶子时就往下一个关键点走,因此有了最后一项,并且肯定是将最深的叶子放在最后最优。

考虑当 \(dep_x > maxdeep\) 时,当 \(B\) 走到叶子节点时,就瞬移到 \(A\),和 \(A\) 一起走到下一个关键点,将这一段的点也看作关键点,因此转移可以写为:

\[\large f_x = \begin{cases} \min\limits_{y\in anc(x)}\left\{ f_y+sum_y-sum_x-(cnt_y-cnt_x)dep_y \right\} & dep_x \leqslant maxdeep \\ f_{fa}+1 & dep_x > maxdeep \\ \end{cases} \]

\(a,b\in anc(x)\),且都满足 \(dep_x \leqslant maxdeep\),考虑两种方案 \(a \rightarrow x\)\(a \rightarrow b \rightarrow x\)。对于 \(subtree(a) \setminus subtree(b)\) 中的叶子,在两种方案的情况是一样的,都是从 \(a\) 前往遍历。对于 \(subtree(b) \setminus subtree(x)\) 中的叶子,使用第二种方案会更优。对于两次方案中最后的那个最深叶子的贡献,第二种方案不会比第一种劣。因此总的来说,第二种方案一定不会比第一种劣,得每次 \(x\) 只需向上找到第一个满足 \(dep_x \leqslant maxdeep\) 的点来转移即可,也就是找到深度最大的满足条件的点。

找这个点的方法是用链表维护出每个点子树内未找到满足条件的点是哪些,\(dfs\) 合并子树时互相来更新,最大深度更浅的子树中的所有点经过更新后都能找到对应的满足条件的点,若最大深度相同,两棵子树中的所有点则都能找到。总时间复杂度为 \(O(n)\)

提交记录

#33. 【UR #2】树上GCD

\(lca(x,y)=x\) 时,其答案很好统计,\(d\) 的答案即为深度 \(\geqslant d\) 的点的个数,这些点和其 \(d\) 级祖先配对后有贡献。

其他情况时,可以统计 \(d\) 的倍数的个数,然后容斥即可。对每个 \(d\) 进行处理,考虑 \(DP\),设 \(f_i\) 表示 \(i\) 子树内到 \(i\) 距离为 \(d\) 的倍数的点的个数且不包括 \(i\) 本身,同时用 \(g_i\) 表示 \(i\) 子树内到 \(i\) 距离在模 \(d\) 意义下为 \(d-1\) 的点的个数来辅助转移,但这样复杂度无法接受。

考虑设阈值 \(S\),当 \(d\leqslant S\) 时使用上面的做法,其他情况时用 \(vector\) 维护每个点子树内每种深度的个数,实现时用启发式合并。当两个子树内最大深度都 \(>d\) 时,枚举 \(d\) 的倍数查询即可,合并次数为 \(O\left(\frac{n}{S}\right)\),每次合并复杂度为 \(O(n\log n)\),因此这部分的复杂度为 \(O\left(\frac{n^2\log n}{S}\right)\)

\(S=\sqrt{n\log n}\),得总复杂度为 \(O(n\sqrt{n\log n})\)

提交记录

#168. 【UR #11】元旦老人与丛林

合法当且仅当对于任意一个点集的导出子图都满足 \(|E|\leqslant 2|V|-2\),即当存在一个点集满足 \(|E|-2|V| > -2\) 时就判定为不合法,于是考虑最大权闭合子图,将边权值看作 \(1\),点权值看作 \(-2\),看最大权值是否 \(>-2\) 即可。如果可以选择空集,则最大权值一定 \(\geqslant 0\),于是枚举每个点强制选择,每次只有一条边发生变化,退流即可。复杂度为 \(O(nm)\)

提交记录

posted @ 2020-12-25 21:14  lhm_liu  阅读(48)  评论(0编辑  收藏  举报