JOISC 2021 简要题解
「JOISC 2021 Day1」饮食区
- 维护 \(n\) 个队列,支持 \(m\) 次操作:
- 在 \([l,r]\) 号队列的尾端均加入 \(k\) 个颜色 \(c\) 的球。
- 将 \([l,r]\) 号队列的前 \(k\) 个球 pop,如果不足 \(k\) 个则清空队列。
- 输出第 \(a\) 号队列第 \(b\) 个球的颜色,如果不足 \(b\) 个球输出 \(0\)。
- \(n,m\leq 2.5\times 10^5,k\leq 10^9,b\leq 10^{15}\)。
一开始 naive 地以为直接删除了就给 \(b\) 记录一个偏移量 \(k\),然后整体二分即可。
唯一不足的地方在于,如果队列清空,就不能加上对应删除的 \(k\),而是只加上 \(\text{size}\)。
正着求这个 \(\Delta\) 不好做,考虑维护每个队列当前的球数 \(x\),以及总共进入过队列的球数 \(y\),那么 \(\Delta = y-x\)。
维护当前队列球数,相当于每次 \(\forall i\in[l,r],x_i\gets \max(x_i+v,0)\)。
这玩意挺经典的,可以直接用矩阵拟合,也不用真的维护矩阵,而是手玩两下得到合并式。
设 \((a,b)\) 表示 \(x_i\gets \max(x_i+a,b)\),\((c,d)\) 同理,那么 \((a,b)\cup (c,d)=(a+c,\max(b+c,d))\)。
「JOISC 2021 Day2」道路建设
- 输出 \(n\) 个点中两两前 \(k\) 小哈密顿距离的值。
- \(n,k\leq 2.5\times 10^5\)。
肯定是一手二分答案。
然后我的思考是硬上三维偏序,实际上判定的时候,相当于 \(n\) 个矩形数点,离线下来即可。
更巧妙的可以转切比雪夫,用一个 set 维护 \(y\) 坐标暴力跳就行,找到一个花费 \(O(\log n)\),找到 \(>k\) 个直接 return false
即可。
「JOISC 2021 Day3」保镖
- 有 \(n\) 个 vip 在直线上行走,表现为从 \(t_i\) 时刻开始从 \(a_i\) 走到 \(b_i\),每个单位时间走一个单位长度。
- 你作为保镖也在行走,如果你某个时刻与某个 vip 位置重合,那么你可以开始保护他,每保护 vip \(i\) 一个单位时间就会获得 \(c_i\) 的收益。
- \(q\) 次询问,每次给出你可能的开始行走的位置和时间,求你最终能获得的最大收益。
- 即使你某个时刻与多个保镖重合,你也只能保护一个。同时,你可以不完整的保护一个 vip,甚至可以不保护他整数单位时间。
- 但是数据保证 \(c_i\) 为偶数,可以证明这种条件下答案也一定为整数。
- \(n\leq 2800,q\leq 3\times 10^6,t,a,b,c\leq 10^9\)。
行走是一维的,但是有时间轴,所以实际上是二维问题,一定记得将问题转化到坐标系上。
先不急,先考虑一个性质:保镖不会停留。
因为停留无非是为了等待一个 vip,但是一直往他的方向走,并且在相遇的那条边先走 \(\frac{1}{2}\) 再折返是不劣的,所以不用停留。
将问题表达到坐标轴上,那么每个 vip 为线段 \((t_i,a_i)\to (t_i+|a_i-b_i|,b_i)\),一定是平行于 \(y=\pm x\) 的。
不如旋转 \(45^{\circ}\),进行 \((x,y)\to (x+y,x-y)\) 的变换(虽然这实际上是:逆时针旋转 \(45^{\circ}\) 并按 \(y=x\) 对称并伸长 \(\sqrt 2\) 倍,但无关紧要)。
发现转化为二维问题也乘了个 \(\sqrt 2\),所以直接令 \(c_i\gets \frac{c_i}{2}\) 就好了。
因为 vip 数很少,所以直接离散化能得到一个 \(2n\times 2n\) 的网格图,边有边权和长度,可以直接把图建出来。
问题转化为:给定网格图,你从某个位置出发,只能往右或往上走,每在一条边上走一个单位长度就能获得对应边权的收益,最大化总收益。
格点可以直接简单 DP,但是你可能是从非格点出发的。
显然最优决策一定是:先沿着一个方向走到某条格线,再沿另一个方向走到最近的格点,之后直接利用格点的 DP 值作为答案。
不同初始方向可以分开求解,以先往右为例。
此时可以每行分开处理,倒序维护,相当于每次插入一次函数 \(y=k_ix + b_i\),每次询问给定 \(x\) 求 \(\max y\)。
可以直接用李超树,因为是插入直线,所以复杂度 \(O((n^2+q)\log n)\)。
实际上能做的更好,转化一下能变成 \(b_i=(-k_i)x+y\),最大化截距 \(y\) 所以维护上凸壳。
同时观察到 \(b_i\) 的单调不降性,所以如果 \(k_i\) 不是递增的,那么一定不优,所以可以理解为 \(-k_i\) 是递减的,即插入点的横坐标递减。
直接维护单调栈,每次二分即可,复杂度 \(O(n^2+q\log n)\)。
「JOISC 2021 Day3」聚会 2
- 对于一个树上有 \(k\) 个点的点集,定义 \(S\) 为它们的决策集合,\(s\) 包含所有到这 \(k\) 个点距离和最小的点。
- 现在 \(k\) 个点可以钦定,求 \(\forall k\in[1,n],|S|_{\max}\)。
- \(n\leq 2\times 10^5\)。
不难发现 \(k\) 为奇数的时候,一定有 \(|S|_{\max}=1\),且这个点是 \(k\) 个点中的一个。
更进一步的,发现 \(k\) 为偶数时,答案就是最长的路径长度,使得路径两端点的子树大小均 \(\geq k/2\)。
统计路径长度使用点分治,每次开桶维护每个子树,桶的大小为 \(\text{siz}_v\),所以可以直接合并,每个桶时刻要取后缀 \(\max\)。
「JOISC 2021 Day4」活动参观 2
- 给定 \(n\) 个活动,每个活动需要占用一段连续的时间 \([l_i,r_i]\)。
- 求字典序最小的长度为 \(k\) 的序列,使得序列中的活动时间无交。
- \(n\leq 10^5,l_i<r_i\leq 10^9\)。
一开始是想着线段树优化建图的,但是应当是假了。
考虑对着字典序贪心,如果加入这一段后,最终还能得到 \(k\) 段就直接加入。
只需要维护函数 calc(l, r)
表示对于子区间 \([l,r]\) 最多能选几个线段,那么就可以直接贪心。
然后这种选不交区间的模型也是经典的倍增优化贪心了。
「JOISC 2021 Day4」最差记者 4
- 每个人三个参数 \((a,h,c)\),分别表示 \(h_i\geq h_{a_i}\),以及修改 \(h_i\) 需要 \(c_i\) 的代价。
- 需要满足所有不等关系,求最小修改代价。
- \(n\leq 2\times 10^5,h_i,c_i\leq 10^9\)。
遇到很多次这种线段树合并优化 树上带前后缀 \(\max/\min\) 之类的转移式了。
一定记住重要思想是,因为是前后缀,只要能取到答案就行,具体点值可以含糊。
显然连出来一棵内向基环树森林,把环拆了,先对子树求解。
一般都先想暴力一点,设 \(f(u,x)\) 表示点 \(u\) 的值取 \(x\) 的最小代价,则:
树上的点值,会取到的只有出现过的点值,所以是 \(O(n^2)\) 的。
对于环上的点,它们的值肯定全都相等,要么等于环上某个点的值,要么 \(=1\),枚举统计即可,这一部分是 \(O(n)\) 的。
这种转移方程一看就是能线段树合并优化的,但是最前面一项 \([x\neq h_u] c_y\) 意味着每次都要进行前后缀加。
线段树合并每次只能修改已有节点或插入有限个节点,这样强行打区间加 tag 肯定是会出问题的。
实际上,简单转化一下,\(f(u,x)\) 表示能保留的最大的代价和,那么:
维护子树 \(\max\),然后右子树对左子树有贡献,同时如果一个节点为空,那就对另一个直接区间加。
显然为了保证复杂度不能继续合并下去,而区间加看似没有对每个位置加上自己的后缀 \(\max\),因为左右均为空时,区间加没贡献上去。
实际无关痛痒,因为每次还是能取到后缀 \(\max\)。也因此,查询 \(f(u,x)\) 的时候要查询整个 \([x,V]\) 后缀。
补集转化的思想还是挺重要的!