JOISC 2017 简要题解

记得是 2022 准备省选那时候开始想版刷点什么,然后就头铁开了 JOISC,还头铁开了 2017。

然后被 Day1 的 3 题干趴下了/kk

算是梦开始的地方吧,2022.11.15 重新整理,为梦起航!

「JOISC 2017 Day 1」开荒者

  • 给定 \(r\times c\) 的网格,其中 \(n\) 个点初始长了草,之后每一天,可以钦定所有草同时向 上/下/左/右 扩展一格。
  • 求最少多少天可以使整个网格被草覆盖。
  • \(n\leq 300,r,c\leq 10^9\)

做题需要切入点,这个最微小的点是最具有启发性的,不通过积累而自发性的思考出来无疑需要天赋。

  • 性质一:草的扩展结果与操作顺序无关。因为当上下左右分别的总次数确定后,无论以任何顺序操作,每个初始节点扩展的都是一个矩形。

这样就只需要考虑 \(u,d,l,r\) 这四个参数。

考虑枚举 \(u,d\),这样每个初始节点会变成一列长条,此时容易贪心的计算出 \(l+r\) 的最小值。

具体方法为,对于每一行,处理出能覆盖到它的列坐标并升序排序,假设为 \(a_1,a_2,\cdots,a_m\),那么:

  • \(A=l\geq a_1-1\)
  • \(B=r\geq c-a_m\)
  • \(C=l+r\geq \max_{1\leq i<m} a_{i+1}-a_i-1\)

最终答案用 \(\max(A+B,C)\) 更新即可。

对行离散化后可以做到 \(\text{calc}(u,d)\) 复杂度为 \(O(n^2)\),考察真正能出现本质不同 \(a\) 序列的 \((u,d)\) 组合,大概能发现只有 \(O(n^3)\) 对。

  • 性质二:设 \(s=u+d\),对于 \(s\) 相等的 \((u,d)\)\(u\) 的不同只会导致图形的整体上下平移,不会改变相对形态。

且不难发现有用的 \(s\) 只有 \(O(n^2)\) 种。

考虑对于每个 \(s\) 计算答案,对于初始有草的点 \((x,y)\),连出 \((x-s,y)\) 的列长条。

然后在这个长度为 \(r+s\) 的图形中截取长度恰为 \(r\) 的连续段,恰好代表了每一种 \((u,d)\) 的情况。

例如在最上端就代表 \(u=0,d=s\),最下端就是 \(u=s,d=0\),从上往下每平移一格等价于 \(u\gets u+1,d\gets d-1\)

对于每一段求答案需要的无非是 \(3\) 个区间 \(\max\),用单调队列可以优化至 \(O(n)\)

但是没完,\(\max_{1\leq i<m} a_{i+1}-a_i-1\) 本身的维护需要 \(O(n^2)\),也就是说每次调用 \(s\) 时预处理甚至比正式处理耗时,总复杂度 \(O(n^4)\)

  • 性质三:将有用的 \(s\) 升序排序后依次考虑,每次只对 \(a\) 数组有变的列重构,总重构次数是 \(O(n^2)\) 的。

这个实现起来可能比较清楚。

因为每次会将 \((x-s,y)\) 变成 \((x-s-\Delta,y)\),唯一能改变 \(a\) 数组的就是这个点在往上延申的过程中经过了某个列的下端点 \((x',y')\)

每次类似于冒泡排序的维护,每个上端点超过一个下端点后就一直在它上方,所以每个上端点至多导致 \(O(n)\) 次重构,总次数就是 \(O(n^2)\)

这样总复杂度就是 \(O(n^3)\) 的了,问题完美解决!

「JOISC 2017 Day 1」港口设施

  • 给定 \(n\) 个货船的进出港口时间,港口的停位是两个栈 \(A,B\)。求合法的钦定每个货船到哪个栈中的方案数。
  • \(n\leq 10^6\)

要有敏感性,栈操作立即相当转化为区间包含、相交、不交等关系。

知道栈是先进后出的,也就是说,\([a,b],[c,d](a<c)\) 能在同一个栈中,要么 \(a<b<c<d\),要么 \(a<c<d<b\)

即,要么无交,要么包含。

然后要能够想到图论建模,将每个相交但不包含的区间之间连边,如果最终是个二分图那么有解。

每个连通块能贡献 \(2\) 的答案,设共有 \(x\) 个连通块,那么答案就是 \(2^x\)。样例也印证了答案都是 \(2\) 的次幂这一点。

如果暴力连边的话,时空都是 \(O(n^2)\) 的。

考虑,如果一个点向多个点连边了,那么之后这多个点都可以缩成一个点处理。

考虑利用线段树维护,对每个区间 \([l,r]\),在包含 \(l\) 的线段树节点处插入 \(r\)

然后再对每个区间 \([l_i,r_i]\) 考虑连边,无非是在 \([l_i+1,r_i-1]\) 中找 \(r>r_i\) 的节点们。

考虑在线段树每个节点处维护大根堆,那么每次连边都是将一些最大值合并,之后用最大的那个代表它们即可。\(O(n\log ^2n)\)\(10^6\) 要有信仰/oh

「JOISC 2017 Day 1」烟花棒

  • \(n\) 个人一排,位置为 \(a_i\),初始时刻第 \(k\) 个人的烟花被点燃,其他人的烟花只能被位置和他重合,且正在燃烧的烟花点燃。
  • 目标是每个人的烟花都被点燃过,所有烟花的燃烧时间都是 \(t\),所有人的移动速度也都一样,求最小移动速度 \(v\),使得能够达成目标。
  • \(k\leq n\leq 10^5,t,a_i\leq 10^9\)

一眼二分答案,但是判定需要思考。观察性质,看上去过程很复杂度。

首先,那些没有点燃的人显然会往中间靠拢。

核心性质:

  • 相遇后立即点燃一定不优于相遇后未点燃的人跟着点燃的人,无论两个人是否分头跑。
  • 也即,全局时刻只有一个烟花在燃烧,或者说,遇到一个人相当于燃烧时间 \(+t\)

同时,因为所有人都在跟着动,所以同向人相对距离不变,反向人减小。

问题就变成了:分别将从 \(k\) 开始 \(k-1\to 1\)\(k+1\to n\) 构建为两个序列,都需要从左往右取,有正有负。保证时刻累加器 \(\geq 0\)

可以发现,如果选择取一个队列,在比初始得到更多的时间之前就回头是不优的,所以可以将每个 \(\geq 0\) 的极小前缀缩为一段,注意会剩下一些不能缩的。

先把能缩的段贪心处理。剩下需要人类智慧了,因为最终时间是明确的,就是 \(\sum (T-a)+\sum (T-b)\),如果它 \(<0\) 显然不行。

然后既然知道了最终结果,那么实际上操作可逆,可以将每次视为消耗 \(t\) 并加回对应代价,要求仍然是累加器时刻 \(\geq 0\)

而且如果正过来不能缩为一段,说明原序列存在一个后缀,使得它的所有前缀和均 \(<0\)

相当于逆向操作之后,剩余序列的后缀和均 \(\geq 0\),说明一定不会有剩余段。所以对于剩下的段,再反过来做一遍几乎相同的操作即可!

「JOISC 2017 Day 2」门票安排

  • 给定一个长度为 \(n\) 的环,有 \(m\) 个人,第 \(i\) 个轮有 \(c_i\) 个人需要从 \(s_i\) 去往 \(t_i\)
  • 显然他们有两种简单路径可以到达目的地,每个人每经过一条边,该边的计数器 \(+1\)
  • 最小化全局计数器的最大值,并输出这个值。
  • \(n\leq 2\times 10^5,m\leq 10^5\)

很棘手的题目,除了 \(2^m\) 枚举每个人的方向之外,初看之下似乎没什么思路。

先寻求一个多项式时间的解,观察到,可以先给每个人随意定一个方向,然后观察如果反向的影响。

为了方便,不妨将每个人初始定向为 \(\min(s_i,t_i)\to \max(s_i,t_i)\)

核心性质 1:如果翻转的两个区间(在翻转前)无交,那么同时翻转它们不优

比较显然,因为同时翻转后,它们原先的部分还是会被覆盖,它们原先没覆盖的部分还多余了贡献。

那么,就可以考虑枚举 \(t\) 表示所有翻转区间都包含 \(t\)。可惜这还是没能帮助我们得到一个多项式时间的方法。

考虑套路的二分答案,在已知枚举的 \(\text{ans},t\) 的情况下,还是不能通过 DP/贪心(有后效性)当常见的多项式时间的判定方法。

突发奇想,可以再枚举一个 \(\text{cnt}\) 表示最终一共翻转了 \(\text{cnt}\) 个区间。

那么根据这 \(3\) 个量,不难通过贪心来判定,更具体的方法:

  • 依次枚举 \(l=1\sim t\),每次对于 \(s_i=l\)\(t_i>t\) 的区间 \(i\),将其 \((b_i,c_i)\) 作为有序对放入大根堆中。

  • 显然,当前情况下,区间 \([l,l+1]\) 当前的答案就是 \((a_i-\text{pre})+(\text{cnt}-\text{pre})\),其中 \(\text{pre}\) 表示当前已经翻转的区间个数。

    \(a_i\) 表示初始情况下, \([i,i+1]\) 被覆盖的次数。

  • 若这个答案 \(>\text{ans}\),就立即尝试不断调整,每次取出堆顶(即最大的 \(r\))操作,因为它能在给最多的区间 \(-\) 的同时给最少的区间 \(+\)

  • 若一切都符合要求,再利用差分得到 \(>t\) 的区间的结果,根据上述贪心过程它们已经被覆盖的尽量少了,若都 \(\leq \text{ans}\) 就返回 \(\text{true}\),反之不然。

以上是这道题的基础的基础(然而已经够难想了 qwq),据此得到了一个 \(O(n^3\log^2 n)\) 的多项式暴力(

发现贪心判定部分已经很优秀了,二分答案也是必须,关键是 \(t,\text{cnt}\) 的枚举量不能接受。

那么,尝试通过分析排除一定无用的 \(t,\text{cnt}\) 成为了目标:

  • 核心性质 2:对于 \(t\),设对于翻转后的答案,每个区间覆盖次数为 \(b_i\),那么 \(b_t=\max b_i\)\(b_t=\max b_i-1\)

    看上去比较神秘,但实际上比较好证。

    设最终所有翻转区间的交为 \([l,r]\)(显然 \(t\in[l,r]\)),只考虑 \(b_t=\max b_{[l,r]}\) 的情况,这不影响整个区间是否被枚举到。

    如果 \(b_t\leq \max b_i-2\),那么考虑同时翻转回来,一个左端点为 \(l\),和一个右端点为 \(r\) 的被翻转过的区间(一定存在)。

    此时 \([l,r]\) 中的 \(\max\) 变为 \(b_t+2\),而外部的全局 \(\max\) 变小了,答案不劣于先前,故该性质得证。

  • 核心性质 3:对于 \(t\),一定有 \(a_t=\max a_i\)

    同样还是设 \(a_t\) 是交区间中最大的。翻转后,显然 \(b_t=a_t-\text{cnt}\),而 \(\forall i\not \in [l,r],b_i=a_i+x\)\(|x|< \text{cnt}\))。

    根据性质 2,有 \(b_t\geq b_i-1\to a_t-\text{cnt}\geq a_i+x\to a_t\geq a_i+x+\text{cnt}\to a_i\geq a_t\)。故得证。

根据性质 \(3\)\(t\) 的取值已经确定为最大的那些 \(a\) 的下标,不失一般性的,选择最左端和最右端的两个即可。

因为如果交的区间同时不包含它们两个,答案总能通过调整变得更优。

根据性质 \(2\),可以发现 \(b_i=a_i-\text{cnt}= \text{ans}(-1)\),故 \(\text{cnt}=a_i-\text{ans}\) 或者 \(a_i-\text{ans}+1\)

也即,\(t,\text{cnt}\) 的枚举量变为了 \(O(1)\),问题迎刃而解。

总结一下:

  • 对于认为不可做的问题,加大枚举量,牺牲复杂度得到一个至少不那么暴力的做法是很有效的。
  • 这里观察到性质一是必要的先行步骤,之后的枚举 \(\text{cnt}\) 是神之一手,

「JOISC 2017 Day 2」火车旅行

  • \(n\) 个站台,每个站台有停靠等级 \(a_i\in[1,m]\)
  • 同时,有无数列等级 \(\in[1,m]\) 的列车在铁路上工作,第 \(i\) 中等级的列车会且只会停靠在 \(a\geq i\) 的站台。
  • 注意,这里的会且只会表明,只要经过了,就要停靠。
  • 多次询问从 \(x\) 站台到 \(y\) 站台,中途至少要停靠多少次(不包含起止点)。
  • \(n,q\leq 10^5\),保证 \(a_1=a_n=m\)

停靠车站等级序列一定是一个上凸的结构,即:\(p_1\leq p_2\leq \cdots \leq p_u\geq p_{u+1}\geq \cdots\geq p_v\)

考虑让每次询问从 \(x,y\) 开始双向奔赴,发现每次既然只会停靠在 $\geq $ 自己当前列车等级的站台,那么每次都下车并乘坐能乘坐的最高等级一定是最优的。

用倍增维护 \(l(i,j)\)\(r(i,j)\) 分别表示从 \(i\) 开始停 \(2^j\) 次最左或最右能到哪里。注意转移的时候可能从 \(r\) 走到 \(l\)\(l\) 还优。

每次询问,互相倍增至极大的不相交位置,之后只要再走一步就能互相到达了。

「JOISC 2017 Day 3」长途巴士

  • 从时间 \(0\) 开始,在时间 \(X\) 结束,一个司机和 \(n\) 个人在进行长途旅行。他们有一个共同的循环周期 \(T\)
  • 每个人有参数 \(D_i\),表示每到时间 \(kT+D_i(k\geq 0)\) 他就需要喝 \(1\) 单位的水。对于司机,他的 \(D_i=0\)
  • 中途有 \(m\) 个休息站,第 \(i\) 个休息站在时间 \(s_i\) 到达。
  • 在每个休息站都可以购买水,一开始的水箱是空的,起点也可以购买水。所有水的价格都是 \(W\) 元每单位。
  • 如果一个人需要饮水时水箱是空的,那么他就会下车,司机需要付出 \(C_i\) 的代价退钱。但司机一定不能下车。
  • 求最优情况下,完成这趟旅途的修小代价。(即 买水的钱 + 退下车人的钱)
  • \(T,X\leq 10^{12},n,m\leq 2\times 10^5,s_i<X\),保证 \(s,D\)\(\bmod T\) 意义下均不重合。

可以发现,如果要赶人下车,那就尽早赶走比较好。

核心性质:如果将人按照 \(D_i\) 升序排序,每次走的人一定是一段连续的区间

如果最后赶走的是第 \(i\) 个人,那么一定是某两个相邻休息站 \(a,b\) 之间,第 \(i\) 个人是最后一个在到 \(b\) 之前要水喝的人。

然后他之前的都可以选择赶下车。

而司机的 \(D=T\) 的作用,就是保证不存在 \([i,n]+[1,j]\) 一段的人被赶走,即保证转移不成环。

既然题目条件这么刻意,那大概率这个思路就是对的了,可以根据这个来做 DP。

\(f(i)\) 表示考虑了前 \(i\) 个人的最小费用,每次转移有两种:

  • \(f(i)=f(i-1)+W(\lfloor X/T \rfloor+X\bmod T\geq D_i)\)。即保留这个人到最后。

  • \(\displaystyle f(i)=\min_{0\leq j<i} f(j)+\text{Sum}_i-\text{Sum}_j+W(i-j)\times \text{Mn}_i\)

    即把 \(j\) 当作一个保留的人划分(显然如果他在决策的时候被赶下车了,枚举前面的时候会枚举到)

    然后考虑 \([j+1,i]\) 全删的代价,\(\text{Sum}\)\(C\) 的前缀和,关键是 \(\text{Mn}_i\) 表示的是最小满足它能被删的时间。

    \(\displaystyle \text{Mn}_i=\min_{D_i< s_j\bmod T<D_{i+1}} \lfloor \frac{s_j}{T}\rfloor\),最后一次是直接被赶下车的不算贡献,所以是下取整。

第一种直接转移,第二种经典斜率优化,\(\text{Mn}_i\) 只需要将 \(s_i\) 按照\(\bmod T\) 升序排序即可一个指针扫一遍求出。

很妙的问题。

「JOISC 2017 Day 3」幽深府邸

  • \(n\) 个房间一排,第 \((i,i+1),1\leq i<n\) 号房间之间有门,需要钥匙 \(c_i\) 才能开。
  • 每个房间有 \(b_i\) 个互不相同的钥匙 \(a_1,a_2,\cdots,a_{b_i}\)
  • 多次询问 \(x,y\),回答能否从 \(x\) 走到 \(y\)
  • \(n\leq 5\times 10^5\)

比较玄学但是看上去又很对的做法。

考虑直接处理出每个房间能走到的区间 \([l_i,r_i]\),就可以做到 \(O(1)\) 回答。

每个区间扩展时,另一个房间能走到的它显然也可以,可以利用这一点快速扩展。

而边界情况下,只需要预处理每个门对应钥匙的前驱后继即可简单判断。可以加个记忆化搜索。

复杂度比较玄学,据说是 \(O(n\log n)\) 的。

「JOISC 2017 Day 3」自然公园

  • 交互题。
  • 无向图有 \(n\) 个节点 \(m\),每次可以问 \((A,B,S)\) 表示只经过集合 \(S\) 中的点能否从 \(A\)\(B\)
  • 要求用 \(\leq 45000\) 次询问确定图中所有的边。
  • 每个点的度数 \(\leq 7\)\(n\leq 14000,m\leq 15000\),有 \(77\) 分为 \(m=n-1\)

做题感觉良好,只写了 \(77\text{pts}\)

一开始想着用点分治,每次随一个当前连通块的点当根,然后找到那至多 \(7\) 个直接相连的点。

然后 \(O(\log 7)\) 确定每个点属于哪个点下面,然后分治下去,复杂度大概 \(O(5n\log n)\) 左右。

喜闻乐见的被卡到只有链的部分分(

实际上,有不基于随机的稳定做法,先还是考虑链上的做法。

发现,只需要每次确定一个在两端点之间的点,然后分治下去即可,确定只需要二分,每个点的确定是 \(O(\log n)\) 的。

所以总复杂度就是 \(O(n\log n)\),应该是几分之一的常数。

对于树上也是类似,首先把 \(0\) 当作连通块中的唯一点,每次枚举一个不在连通块的点。

然后一次二分找到该点到连通块的最近点,然后就和序列一样了,可以确定整条树链。

复杂度是 \(O(2n\log n)\),同样也显然跑不满。这个思想还是挺有趣的!

「JOISC 2017 Day 4」绑架 2

  • 遵循以下规则在 \(n\times m\) 的网格图中移动,每行每列均有一个车流指数。
  • 移动开始时,可以任意选择方向。
  • 当到达十字路口时:
    • 如果「直行方向的道路的车流指数」比「该十字路口的另一条道路的车流指数」小,就转弯。你可以选择左转还是右转。但如果你在城市边界上,可能只能左转/右转。
    • 如果「直行方向的道路的车流指数」比「该十字路口的另一条道路的车流指数」大,就直行。但如果前面没路(比如到了城市边界),就只能停在此处。
    • 不能掉头。
  • \(q\) 次询问,求从某个点出发最远能移动多远。
  • \(n\leq 5\times 10^4,q\leq 100\)

部分分连个 DAG 跑最长路,和正解无关。

正解更简单,直接记忆化一下,倍增加速左右/上下找第一个拐点的过程。

似乎可以证明状态数是 \(O(N\sqrt Q)\) 的,总复杂度 \(O(N\log N+N\sqrt Q\log N)\)。实际跑起来飞快。

update:这题还有很喵呜喵呜的分治做法,每次根据子矩形内流量最大的直线来分割,如果询问恰好在直线上就简单了,否则分治下去,复杂度严格 \(O((n + m) q)\)

posted @ 2022-11-15 11:34  LPF'sBlog  阅读(678)  评论(0编辑  收藏  举报