zoukankan      html  css  js  c++  java
  • 「SOL」JOISC2021 解题报告

    JOIS(egment-Tree)C

    1. 前言

    很早之前教练让我们做这套题,我以为这套题应该挺简单,用几天的空余时间就能刷完,结果预想的短周期刷题变成了长周期刷题……(好像是整个团队里最后一个刷完的??)

    大多数题目(除了「保镖」和「特技飞行」,我不知道把特技飞行这种题放 Day1 是不是想搞选手心态 QwQ)还是能够独立地想出来,但是代码长度堪忧,看了一下好像每道题的代码都比其他人要长一些,不知道是不是实现细节的问题。话说过来虽然代码要长一些,但是运行效率好像要快一些耶,目前「IOI 热病」和「最差记者 4」都是 LOJ 的速度榜一 awa。

    这一发是把线段树给练爽了,真就 JOI Segment-Tree Camp 呗。


    2. Day1 解析

    2.1. IOI 热病

    2.1.1. 评析

    推导「每个人行走方向其实固定」这一点比较考察贪心构造的技巧。

    而后面「李超线段树上查全局最小值,支持删除坐标」这个有一定套路(我自己的做法,可能比较复杂),但是主要还是考察代码实现能力。

    2.1.2. 解析

    先枚举第一个人往哪个方向走,然后对其他人的最优行走方向进行分析。

    不妨设 ta 向上走,先考虑一些特殊的位置——考虑到能一个人与一个人相遇的人是米字格形状的,所以先分析以起点为中心的米字格形状。

    • 显然 1 区域只能向下,5 区域只能向上
    • 分析 2 区域,首先不可能向右向上;若向下,有一个非常巧妙的分析是「在时刻 (i) 出现感染的范围是距离起点横纵坐标之差不超过 (i) 的位置」,手动模拟一下「可能出现感染的范围」和 2 区域的人行走的情况,发现不可能被感染;于是只能向左。同理 8 区域只能向右
    • 分析 3 区域,不可能向右向下;若向上,同样模拟「可能出现感染的范围」和 3 区域行走的情况,不可能感染,所以只能向左。同理 7 区域只能向右
    • 分析 4 区域,不可能向右向下;若向左,同样模拟「可能出现感染的范围」和 4 区域行走的情况,发现只能向上。同理 6 区域只能向上

    分析得到这些区域只可能向一个方向走,于是我们大胆猜测其他区域也是这样。实际上利用「可能出现感染的范围」的分析同样可以得到以下结论:

    于是可以直接枚举第一个人走的方向,然后固定其他人走的方向。

    接下来其实就是模拟感染的过程了。两个人相遇时可能会发生感染,但是必须要有一个人已经感染。记 (d_i) 表示第 (i) 个人被感染的时刻。则在 (ge d_i) 的时刻,第 (i) 个人才具备感染能力。发现 (d_i) 其实就是最短路

    考虑用 Dijkstra 求最短路,设当前点为 (u=(x_u,y_u))。不妨设 (u) 的行走方向为 (y) 正方向(即「向上」),(u) 能够更新到的点只有:

    • 左上右上的对角线上的人,若这个人在 (v=(x',y')),则相遇时间为 (|x'-x_u|),当 (|x'-x_u|ge d_u) 时会感染,更新 (|x'-x_u|overset min o d_v)
    • (u) 行走方向上的人 (v=(x_u,y')),则相遇时间为 (frac {y'-y_u}2),当 (frac {y'-y_u}2ge d_u) 时会感染,更新 (frac {y'-y_u}2overset min o d_v)

    我们发现每次更新,都是将对角线上或同行同列上,(x) 坐标(或 (y) 坐标)在某个范围内的点的 (d) 更新为一个关于 (x)(y) 的一次式。于是可以对每条对角线、每行每列维护一棵李超线段树,每次区间插入一条直线,支持查找全局最小点。一个区间的最小点一定在端点取到,直接更新即可。

    这样更新是支持了,但是 Dijkstra 还需要「弹出当前点」,也就是将当前这个 (d) 最小的点删除。李超线段树不是不支持删除吗?李超线段树不支持删除已经插入的直线,但是可以删除要考虑的点。我们可以给已经删除的点打标记,更新区间最小值时找到左右两端的未被删除的点,这种「只有删除,查找下一个未删除的点」的操作实际上是一个并查集的套路。

    可能就是并查集运行效率非常高,使我的代码运行速度比其他人要快吧。

    时间复杂度瓶颈在于李超线段树区间插入线段,(mathcal O(nlog^2n)),常数较大。

    2.1.3. 源代码

    考虑到直接粘贴代码会使文章长度爆炸,这里直接附上 LOJ 的提交记录。

    > Link LOJ - IOI热病 参考代码

    2.2. 饮食区

    2.2.1. 评析

    部分分算法特别多,比较考验选手对各种算法的熟悉程度。

    一开始我想的是分块,然后空间卡不过去……

    2.2.2. 解析

    删除操作非常麻烦。如果没有删除操作,那么我们可以整体二分,对每个查询找到第一次队列的人数大于等于 (B_i) 的时候。

    考虑删除会产生什么影响。我们尝试「不真的进行删除操作」,而是把查询给定的位置 (B_i) 向后移动离开的人数。注意到队列可能小于 (K_i),不能直接向后移动 (K_i)

    用线段树分别维护「不考虑删除操作,当前队列有多少人」,以及「考虑删除操作,当前队列有多少人」。第一种就是区间加、单点查;第二种稍麻烦,每次有删除操作时,需要全部元素对 (0)(max),但是只涉及单点查询,可以直接维护形如 (x'=max{x+a,b}) 的懒标记 ((a,b)),而不用 Segment Tree Beats! 。

    两个值的差就是询问前离开的人数,然后就可以整体二分了。注意需要提前判断是否无解。

    时间复杂度 (mathcal O(nlog^2n))

    2.2.3. 源代码

    > Link LOJ - 饮食区 参考代码


    3. Day2 / Day3 解析

    因为通信题暂时不做,然后「保镖」这道题又完全不会,Day2Day3 就合起来了……

    3.1. 道路建设

    3.1.1. 评析

    这道题硬上复杂数据结构(树套树、KD树)也能做,但是在考场上比较消耗时间。

    如果多花些时间思考有没有简单一些的方法,反而可能节省时间。毕竟出题人只要不是想要出防 AK 题会考虑代码的复杂程度。

    3.1.2. 解析

    显然是要用数据结构直接维护当前花费最小的方案,支持把最小的方案删除。

    还是比较常见的套路,对每个点求出从它出发的最优方案,全部塞进堆里。每次从堆里取出全局最优方案,将其删除后找到对应点的次优方案。

    曼哈顿距离有两个绝对值,如果硬上数据结构就需要树套树维护四个象限的点。但是题目规定起点终点交换本质相同,我们可以直接把点按 ((x,y)) 双关键字排序,只考虑从较大的点出发到较小点的路径。这样一来 (x) 的绝对值就没了。

    现在的问题就是维护 ((x,y)lt(x_u,y_u)) 的所有点中,(y) 在某个区间上的点的 (max{-x-y}) 以及 (max{-x+y})。需要支持删除指定的点。

    不管删除操作,那就是可持久化线段树。加上删除操作呢?那还是可持久化线段树,只在当前线段树上删除指定节点,把对应位置赋值为 (-infty) 即可。

    时空复杂度为 (mathcal O((n+K)log n))

    3.1.3. 源代码

    > Link LOJ - 道路建设 参考代码

    3.2. 聚会 2

    3.2.1. 评析

    个人认为是一道比较一般的题目……仅仅是考察基础的树上信息维护的方法而已。

    3.2.2. 解析

    设出席者的点集为 (S),通过调整法(如果「开会地址」不符合下述条件,可以通过把它向某个方向调整,使代价不减,直到符合下述条件,即为代价最小的「开会地址」)可证明:

    • (|S|) 为偶数,则「开会地址」可以取某条路径上的所有点;
    • (|S|) 为奇数,唯一的「开会地址」是 (S) 中距离其他点距离之和最小的点。

    所以我们只需要回答 (|S|) 为偶数的的情况。不妨枚举上述的「某条路径」为 ((u,v))

    (S) 可取的点集为 ({u,v}cup S_1cup S_2),并且 (S)({u}cup S_1) 中的点数必须和 ({v}cup S_2) 中的点数相等,类似于中位数。于是 (dist(u,v)) 可以贡献到 (|S|le2(min{|S_1|,|S_2|}+1)) 的所有偶数 (|S|) 的答案。

    考虑树形 DP,(f(u,s)) 表示 (u) 子树内,子树大小大于等于 (s) 的最深的点的深度。一边转移一边贡献到答案。(s) 不超过 (u) 的子树大小,可以考虑 Dsu on Tree 进行优化,时间复杂度 (mathcal O(nlog n))

    但是我们发现 Dsu on Tree 只能计算「折线型」的路径,而不能计算祖先到后继的路径。分别考虑较小值在后继方向和在祖先方向的情况:

    • 后继的子树较小:在 DFS 时,用线段树维护子树大小大于后继子树的祖先的最小深度,或者利用单调性二分;
    • 祖先的子树较小:直接线段树合并维护出当前子树内,子树大小在某个区间内的后继的最大深度。

    复杂度仍然是 (mathcal O(nlog n))

    3.2.3. 源代码

    > Link LOJ - 聚会 2 参考代码


    4. Day4 解析

    4.1. 活动参观 2

    4.1.1. 评析

    考察了非常经典的字典序的贪心性质以及选手选择算法的能力。

    与道路建设一题相同,用较简单的算法往往可以节省大量时间。

    4.1.2. 解析

    先读对题,字典序是将参加的活动按编号排序后再比较,而不是按参加时间比较。

    于是贪心地从小到大判断「是否可以参加第 (i) 个活动」。

    假设要参加,首先 (i) 不能和已经参加的活动冲突。

    然后 (i) 会把原来的一个空闲时间段 ([L,R]) 分成两段 ([L,l_i],[r_i,R]),记只考虑时间段 ([L,R]),最多能够参加的活动数量为 (f(L,R))。则原本 ([L,R]) 提供 (f(L,R)) 的贡献,现在只能提供 (f(L,l_i)+f(r_i,R)+1) 的贡献。直接判断剩余次数是否足够即可。

    怎么计算 (f(L,R))?贪心地选取活动,每次选取可参加的右端点最小的活动,于是参加一个活动之后的下一个活动(的右端点)是唯一的,倍增即可。

    4.1.3. 源代码

    > Link LOJ - 活动参观 2 参考代码

    4.2. 最差记者 4

    4.2.1. 评析

    一道不错的线段树合并优化 DP 的题,对这个技巧比较熟练的话应该能一眼看出来。

    如果想到利用 DP 数组的单调性维护差分数组,可以在一定程度上简化代码,但是没想到这点也能做。

    4.2.2. 解析

    把选手的 rating 的 “≥” 关系建为有向图,(u o v) 表示 (u) 的 rating 小于等于 (v) 的 rating。

    每个点的入度为 (1),显然每个连通块是叶向基环树,不同连通块之间没有影响。基环树环上的点的 rating 必须相同,显然最终环的 rating 只可能是 (1) 或者环上某个点原本的 rating。

    最后枚举环的取值,考虑对树的部分进行 DP。定义一个非常暴力的 DP 状态,(f(u,w)) 表示 (u) 的 rating 设为 (w),子树内的总花费的最小值。

    [f(u,w)=sum_{v}min_{kge w}{f(v,k)}+[w eq h_i]c_i ]

    然而这样定义会使得 (f(u,w)) 有值的点很多,并且被分成前后缀两个部分。考虑到 (c_i) 之和为定值,我们可以反过来定义 (f(u,w))(u)(w)(u) 子树内减免的花费的最大值。

    [f(u,w)=sum_vmax_{kge w}{f(v,k)} + [w=h_i]c_i ]

    这样比较方便之后线段树合并。

    转移式中有后缀 (max),并且是子树求和合并的形式,可以用线段树合并。我们发现,后缀 (max) 使得大多数位置都有值,如果在合并是直接新建节点下放懒标记,会导致线段树节点数爆炸,破坏线段树合并的时间复杂度。但是后缀 (max) 改变的位置数量只有子树大小,称改变的位置为「关键点」,则只需要维护关键点的值即可。

    比较显然的是 (v_1,v_2) 合并后的关键点就是 (v_1,v_2) 各自的关键点的并,于是在合并时维护两棵线段树各自的后缀 (max) 即可。合并时若出现一棵线段树已经为空,会对另一棵线段树产生「整体加上后缀 (max)」的贡献,这意味着要打加法标记。我们并不希望下放标记,还好加法标记比较简单,可以采用标记永久化

    整个代码最复杂的部分就是线段树合并的函数,具体可以参考代码。

    4.2.3. 源代码

    > Link LOJ - 最差记者 4 参考代码


    THE END

    Thanks for reading!

    给我 点时间就好
    让我 安静地坠落
    请不要担心我
    并不需要太久
    然后装作
    没发生过

    ——《难过233秒》By ChiliChill

    > Link 难过233秒 - 网易云

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    Pyhton 单行、多行注释方法
    laravel中不使用 remember_token时退出报错,如何解决?
    PHP实现打印出库单,有没有实现过?
    是不等号的意思
    PHP如何输出合并单元格的表
    一起谈.NET技术,.Net创建Excel文件(插入数据、修改格式、生成图表)的方法 狼人:
    一起谈.NET技术,ASP.NET MVC 通过 FileResult 向浏览器发送文件 狼人:
    一起谈.NET技术,asp.net Ajax AutoComplete控件使用 狼人:
    一起谈.NET技术,Silverlight 拖动复制控件 狼人:
    一起谈.NET技术,ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇] 狼人:
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14956845.html
Copyright © 2011-2022 走看看