Solution Set - NOI真题
NOI2024 RP++!
NOI2021
Day1T1
对整棵树做轻重链剖分。每一次修改,找到轻标记边集合中涉及到该路径的边删掉。然后,拿出重链上的所有轻边和重链。,把重链上除了底部的点全部标记为“到重儿子的边是标记边”,把所有轻边加入轻标记边集合。每一次查询,查询重链上有多少个点满足上述标记,再找所有轻边有多少是标记边。
到重儿子的标记边可以用线段树维护。关键在于如何处理轻标记边。可能删除的所有轻标记边,一种是 LCA 与它父亲结点的连边,另一种是路径上的某个点到它儿子结点的连边。于是也可以用线段树维护,在拆分出的每条重链上二分。
复杂度是 的。需要卡常,但是被卡常了也有 分。
Day1T2
大名鼎鼎的 LGV 引理模板题。LGV 引理指出,在一个 DAG 中,给定 个起点 和 个终点 ,设 表示对于 的排列 ,选择 到 的 条路径,且它们没有公共点的方案数。则所有偶排列的 值的和减去所有奇排列的 值的和可以用一个行列式来刻画。具体来说,设从 到 的路径数目为 ,那么 这个矩阵的行列式的值就是上述差值。知道了这个引理之后,这题就是明显的模板题了。
Day1T3
首先划分强连通分量。根据题目性质,容易发现缩点后的图构成一棵外向。考虑树上问题,如果不加边,则 能到的点是它的子树,能到 的点是它的祖先。加入 条边, 能到的点可能会变成 个子树,能到 的点可能会变成 个点到根的链的并。做树剖,把后者拆成若干个区间,做区间合并,然后把两组区间求交集大小。这样整个问题就在 的时间内完成了。
Day2T1
均分为 段,则如果能够匹配,至少有一段是对上的。数据随机,所以直接枚举至少有一段对上的所有串就可以了。可以用位运算压一压。
Day2T3
首先有一个简单暴力,就是直接容斥合法的起点集合。这样复杂度是 的。观察一下,发现如果一个机器人右移的步数比较少,那么它影响的位置就比较少;如果它右移的步数比较多,那么它可能的起点集合就比较小。于是,对于右移长度 的部分,我们直接容斥;对于右移长度 的部分,也就是只用考虑不超过 个位置影响的部分,枚举这个长度,做状压 DP。这样复杂度就大概是 ,还不足以通过。剩下的部分再用 bitset 优化就可以了。
NOI2020
Day1T1
好像是一个神秘做法。我们要考虑的就是从 到 经过 天的最大收益,然后再对 个特殊的时刻做 DP 就可以了。这个最大收益当然也可以做 的 DP,但是 太大。考虑最优解长成什么样,一定是在从 到 的某条简单路径上,从某个地方开始拉到一个环上,然后在这个环上打转。注意到我们只用考虑 所在的强连通分量,而同一个强连通分量内能到达的环是相同的。所以只用找到这个最优的环就可以确定最优解了。写法上是先预处理出比较小的 的值,然后找等差数列。正确性不知道,感觉很假。
Day1T2
首先注意到对所有以同一个点为下端点的路径,只用保留上端点深度最深的一个。设 点的深度为 ,所有形如 的路径中 的深度最大值是 。从容斥的角度入手,设计 DP,设 表示选择了一些下端点在子树 内的路径,覆盖了 到深度为 的祖先的部分和子树 内 条边。转移形如树上背包,具体是 。答案是 。发现 这一维可以压掉,转移就变成 。用前缀和优化一下,可以得到最后的转移式是 。再考虑到容斥时的 系数,会发现对于每个 只能保留 的部分。最后这个式子考虑用线段树合并优化,修改都是区间加法,合并是对应位置相乘。只要维护加法标记和乘法标记就可以了。
Day2T1
首先 的时候可以直接贪心,每次取出最小的全用完,取出最大的补齐。容易证明一定是合法的。 时,考虑连边,会得到至少两个连通块。可以知道实际上就是要分成两个 的情况,每一部分 的和要等于 。那就是一个背包问题,用 bitset 优化即可。
Day2T2
考虑哪些树是有用的。会发现那些每个点的左右儿子的 size 的 min 都不超过 的树是关键的。具体来说,如果只有有限个这样的树不能被生成,那么总共就只有有限个树不能被生成。称这样的树为好树,会发现非好树不可能长出好树,所以就不用考虑。现在递归向下,所有的树分成四类:只有左儿子,只有右儿子,左儿子为 ,右儿子为 (每一类不包含前面的类),分别递归下去即可。每个树最多被递归深度次,而深度只能是 的,所以最终复杂度大概是线性的。
NOI2019
Day1T1
和 APIO2024T2 简直就是一道题,不过这道题要更简单一点。考虑把每趟车出发与到达的地点和时间揉在一起作为一个结点,建图,一些边表示车次,另一些边表示在某个点等待一段时间。这样得到的是一个 DAG。因为等待的代价是一个二次函数,所以每一次等待都必须是完整的一段,那样边数就到了 级别,不能接受。所以考虑直接按照 DAG 的拓扑序,也就是时间处理。每遇到一个出发的时刻,就在这个时刻之前,位于该点的到达时刻中,选择等待到当前时刻代价最小的一个。稍加分析可以发现这个东西是举有决策单调性的,所以每个点维护一个单调栈就可以了。
Day1T2
设 分别表示上一个,下一个大于 的位置(如果相等,位置更前的较小),那么要求就是 。丢到笛卡尔树上去考虑,可以得到一个暴力的 DP:设 表示填满区间 ,该区间最大值不超过 的方案数。转移就是 。
考虑两个优化:首先涉及的区间不会很多,最多不超过 ,可以离散化下来;然后 这一维的值域很大,但是 值是关于 的分段函数,分段点是所有的 和 ,并且每一段内都是 次的多项式,所以可以插值。然后就可以做到 了。
Day1T3
谔谔,怎么是模拟费用流,会不了一点口牙。
考虑如下费用流模型:
- ,;
- ,表示选择这一对相同的下标;
- ,,,表示其它的下标最多选 个。
考虑模拟费用流,每一轮增广会选择两个未选择的点 改为选择,过程中有如下 种情况:
- ,即选择某一对 ;
- ,即选择某一对 ,耗费一单位自由流量;
- 将 与 , 与 配对,要求 都已经配对,增加一单位自由流量;
- 将 与 配对, 与 原来配对的 配对。
- 将 与 配对, 与 原来配对的 配对。
于是使用五个堆维护:两侧的 , 的 ,本侧未选另一侧选的 。
Day2T1
显然这就是一个最短路问题。考虑做一个 dijkstra,优先队列里面存放当前所有的边(点到矩形),每次取出一条边,就更新矩形内所有还没有更新过的点的最短路,并把它们删掉。查询点可以用线段树套 set 维护,复杂度 。
Day2T2
大胆猜测结论:一次函数洗牌后的期望还是一次函数,二次函数洗牌后的期望还是二次函数。然后只用递推前三项就可以了。
NOI2018
Day1T1
考虑一个最高的水位线使所有点通过没有积水的边就可以连通,也就是求出了一棵海拔的最大生成树。会发现只有这棵树上的边是可能开车经过的。建出 Kruskal 重构树,则固定水位时,每个点开车能到达的点是重构树的一棵子树。预处理出每个点走到 号点的最短路,然后查询子树最小值即可。至于确定这棵子树只需要倍增。
Day1T2
容易发现好排列等价于最长下降子序列不超过 。考虑 DP,设 表示前 个数的最大值为 ,此后的方案数。下一个数如果填大于 的数,方案数为 ;如果填小于 的数,就只能填最小值,方案数为 ,当然在 的时候没有这种转移,可以认为 。因此,对所有 ,。
考虑这个东西的组合意义: 表示从 开始,每一步可以走到下一列的更高处,不跨过 ,到达 的方案数。 也就等价于每次向右向上走一个单位长度,走到 的方案数。这是卡特兰数的经典模型。利用相同的证明方式,可以求出 。
最后来考虑字典序的限制。来求前 位都相同,第 位更大的方案数。设此前的最大值为 ,未出现的最小值为 ,当前 。容易发现方案数应该是 。但是当 时,此后不再有合法方案。
时间复杂度 。
Day1T3
一个简单的问题是如何求 和 的公共子串数目。只要把 放在 的 SAM 和 的 SAM 上同时匹配即可。这里加一个线段树,判断是否有 endpos 在区间内即可。注意要暴力地减少长度而不是直接跳 slink,但是数据好像很水怎么写都能过。
Day2T1
用一个 multiset 模拟容易求出每一次选择的攻击力 。然后问题等价于求最小的 ,使得 ,且 。使用 exCRT 求解即可。
NOI2017
Day1T1
显然此题应当使用数据结构维护二进制位。那么直接使用 std::set
去维护所有极长的连续段。修改时把每个数拆成 个二进制位依次修改,根据加减讨论一下即可。该做法需要大力卡常。不过现在CCF的机子已经很快了来着?
Day1T2
直接哈希就可以了。用链表维护,连接两个序列的时候计算交界处新产生的串的哈希值,插入哈希表里。断开两个序列时同理在哈希表中减掉。因为断开操作很少,每一次断开最多增加 个串串,而初始是 个串,所以总的串串数目是 的,总时间复杂度就是 ,哈希表的常数忽略不计。
Day1T3
来求最大矩形面积不超过 的概率。因为选择的矩形一定要贴紧下边界,所以只需要关心每一列底部合法的高度。进一步,一个高度为 的矩形是由连续一段高度 的部分构成的。那就可以设计 DP 状态: 表示已经有一段长度为 ,高度 的极长连续段,这部分合法的概率。转移枚举第一个高度等于 的位置 ,有 。这个 也可能不存在,有 。直接这样做的时间复杂度是 的,因为合法的状态只有 ,所以只有 的部分复杂度高。而 的部分是一个常系数齐次线性递推,可以用多项式取模进行优化。总的时间复杂度 。
Day2T1
“用车规则”的限制明示这道题应该使用 2-SAT 做。如果没有适合所有赛车的地图,那么直接 2-SAT 就做完了。而适合所有赛车的地图数量很少,所以可以枚举,将所有 换成 或 。容易发现这样不会漏解。时间复杂度 。
Day2T2
好神仙一题。
首先考虑“时间倒流”,每天会加入一些菜,菜可以无限期保存。这样“变质”就丢掉了。
然后建立费用流模型:
- 为第 天的第 种蔬菜建立点 ,连边 ,边权为 ,容量为当天加入的菜的数目。
- 连边 ,表示可以将蔬菜留到下一天。
- 为第 天建立一个点 表示出售,连边 ,连边 ,容量为 。
- 在菜 第一次出现的时间,从 中分出一个流量,边权改为 。
然后求解最大费用任意流。
接着通过模拟费用流找到反悔贪心。考虑从后向前依次加入每一天的出售点,观察会新形成怎样的“源汇路”或“环”。
结论是 的出边不会退流。也就是说,在未“时光倒流”的原问题中,每一天售卖蔬菜的最优方案包含上一天的最优方案。于是只用求出 天的最优方案,保留其中最贵的 棵就得到 天的最优方案。
至于如何求出前 天的最优方案,回到费用流模型。这次可以换一个顺序处理:每次加入一天的所有蔬菜和出售点。可能的决策有两种:卖掉今天到达的某个菜,或者卖掉之前存下来的某个菜。于是拿一个堆维护一下所有菜的集合即可。
还是好抽象啊
Day2T3
Link.
谁家好人 NOI 出计算几何啊
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析