NOI2024 RP++!
NOI2021
Day1T1
对整棵树做轻重链剖分。每一次修改,找到轻标记边集合中涉及到该路径的边删掉。然后,拿出重链上的所有轻边和重链。,把重链上除了底部的点全部标记为“到重儿子的边是标记边”,把所有轻边加入轻标记边集合。每一次查询,查询重链上有多少个点满足上述标记,再找所有轻边有多少是标记边。
到重儿子的标记边可以用线段树维护。关键在于如何处理轻标记边。可能删除的所有轻标记边,一种是 LCA 与它父亲结点的连边,另一种是路径上的某个点到它儿子结点的连边。于是也可以用线段树维护,在拆分出的每条重链上二分。
复杂度是 \(O(n\log^2 n)\) 的。需要卡常,但是被卡常了也有 \(95\) 分。
Day1T2
大名鼎鼎的 LGV 引理模板题。LGV 引理指出,在一个 DAG 中,给定 \(n\) 个起点 \(P_i\) 和 \(n\) 个终点 \(Q_i\),设 \(f(\sigma)\) 表示对于 \(1,\cdots,n\) 的排列 \(\sigma\),选择 \(P_i\) 到 \(Q_{\sigma_i}\) 的 \(n\) 条路径,且它们没有公共点的方案数。则所有偶排列的 \(f\) 值的和减去所有奇排列的 \(f\) 值的和可以用一个行列式来刻画。具体来说,设从 \(P_i\) 到 \(Q_j\) 的路径数目为 \(d_{ij}\),那么 \(d\) 这个矩阵的行列式的值就是上述差值。知道了这个引理之后,这题就是明显的模板题了。
Day1T3
首先划分强连通分量。根据题目性质,容易发现缩点后的图构成一棵外向。考虑树上问题,如果不加边,则 \(s\) 能到的点是它的子树,能到 \(t\) 的点是它的祖先。加入 \(O(1)\) 条边,\(s\) 能到的点可能会变成 \(O(1)\) 个子树,能到 \(t\) 的点可能会变成 \(O(1)\) 个点到根的链的并。做树剖,把后者拆成若干个区间,做区间合并,然后把两组区间求交集大小。这样整个问题就在 \(O(n\log n)\) 的时间内完成了。
Day2T1
均分为 \(16\) 段,则如果能够匹配,至少有一段是对上的。数据随机,所以直接枚举至少有一段对上的所有串就可以了。可以用位运算压一压。
Day2T3
首先有一个简单暴力,就是直接容斥合法的起点集合。这样复杂度是 \(2^n\) 的。观察一下,发现如果一个机器人右移的步数比较少,那么它影响的位置就比较少;如果它右移的步数比较多,那么它可能的起点集合就比较小。于是,对于右移长度 \(\ge \frac{n}{2}\) 的部分,我们直接容斥;对于右移长度 \(\le \frac{n}{2}\) 的部分,也就是只用考虑不超过 \(\frac{n}{2}\) 个位置影响的部分,枚举这个长度,做状压 DP。这样复杂度就大概是 \(O(nm2^{n/2})\),还不足以通过。剩下的部分再用 bitset 优化就可以了。
NOI2020
Day1T1
好像是一个神秘做法。我们要考虑的就是从 \(u\) 到 \(v\) 经过 \(t\) 天的最大收益,然后再对 \(k\) 个特殊的时刻做 DP 就可以了。这个最大收益当然也可以做 \(O(nmt)\) 的 DP,但是 \(t\) 太大。考虑最优解长成什么样,一定是在从 \(u\) 到 \(v\) 的某条简单路径上,从某个地方开始拉到一个环上,然后在这个环上打转。注意到我们只用考虑 \(1\) 所在的强连通分量,而同一个强连通分量内能到达的环是相同的。所以只用找到这个最优的环就可以确定最优解了。写法上是先预处理出比较小的 \(t\) 的值,然后找等差数列。正确性不知道,感觉很假。
Day1T2
首先注意到对所有以同一个点为下端点的路径,只用保留上端点深度最深的一个。设 \(u\) 点的深度为 \(dep_u\),所有形如 \((x,u)\) 的路径中 \(x\) 的深度最大值是 \(mx_u\)。从容斥的角度入手,设计 DP,设 \(dp_{u,i,j}\) 表示选择了一些下端点在子树 \(u\) 内的路径,覆盖了 \(u\) 到深度为 \(i\) 的祖先的部分和子树 \(u\) 内 \(j\) 条边。转移形如树上背包,具体是 \(dp_{u,i,j}dp_{v,x,y}\to dp_{u,\max(i,x-1),j+y+[x\neq dep_v]}\)。答案是 \(\sum 2^{n-1-j}dp_{1,0,j}\)。发现 \(j\) 这一维可以压掉,转移就变成 \(2^{[x=dep_v]}dp_{u,i}dp_{v,x}\to dp_{u,\max(i,x-1)}\)。用前缀和优化一下,可以得到最后的转移式是 \(f_{u,i}(f_{v,i}+f_{v,dep_v})\to f_{u,i}\)。再考虑到容斥时的 \(-1\) 系数,会发现对于每个 \(u\) 只能保留 \(i\in[mx_u+1,dep_u]\) 的部分。最后这个式子考虑用线段树合并优化,修改都是区间加法,合并是对应位置相乘。只要维护加法标记和乘法标记就可以了。
Day2T1
首先 \(m\ge n-1\) 的时候可以直接贪心,每次取出最小的全用完,取出最大的补齐。容易证明一定是合法的。\(m=n-2\) 时,考虑连边,会得到至少两个连通块。可以知道实际上就是要分成两个 \(m=n-1\) 的情况,每一部分 \((k-a_i)\) 的和要等于 \(k\)。那就是一个背包问题,用 bitset 优化即可。
Day2T2
考虑哪些树是有用的。会发现那些每个点的左右儿子的 size 的 min 都不超过 \(1\) 的树是关键的。具体来说,如果只有有限个这样的树不能被生成,那么总共就只有有限个树不能被生成。称这样的树为好树,会发现非好树不可能长出好树,所以就不用考虑。现在递归向下,所有的树分成四类:只有左儿子,只有右儿子,左儿子为 \(1\),右儿子为 \(1\)(每一类不包含前面的类),分别递归下去即可。每个树最多被递归深度次,而深度只能是 \(O(\sqrt n)\) 的,所以最终复杂度大概是线性的。
NOI2019
Day1T1
和 APIO2024T2 简直就是一道题,不过这道题要更简单一点。考虑把每趟车出发与到达的地点和时间揉在一起作为一个结点,建图,一些边表示车次,另一些边表示在某个点等待一段时间。这样得到的是一个 DAG。因为等待的代价是一个二次函数,所以每一次等待都必须是完整的一段,那样边数就到了 \(O(n^2)\) 级别,不能接受。所以考虑直接按照 DAG 的拓扑序,也就是时间处理。每遇到一个出发的时刻,就在这个时刻之前,位于该点的到达时刻中,选择等待到当前时刻代价最小的一个。稍加分析可以发现这个东西是举有决策单调性的,所以每个点维护一个单调栈就可以了。
Day1T2
设 \(l_i,r_i\) 分别表示上一个,下一个大于 \(a_i\) 的位置(如果相等,位置更前的较小),那么要求就是 \(|(l_i+r_i)-2i|\le 2\)。丢到笛卡尔树上去考虑,可以得到一个暴力的 DP:设 \(dp_{l,r,x}\) 表示填满区间 \([l,r]\),该区间最大值不超过 \(x\) 的方案数。转移就是 \(dp_{l,r,x}=\sum_{|l+r-2d|\le 2}\sum_{i=a_d}^{\min(b_d,x)}dp_{l,d-1,\le i}dp_{d+1,r,\le i-1}\)。
考虑两个优化:首先涉及的区间不会很多,最多不超过 \(m=2500\) ,可以离散化下来;然后 \(x\) 这一维的值域很大,但是 \(dp\) 值是关于 \(x\) 的分段函数,分段点是所有的 \(a_i\) 和 \(b_i+1\),并且每一段内都是 \(r-l\) 次的多项式,所以可以插值。然后就可以做到 \(O(n^2m)\) 了。
Day1T3
谔谔,怎么是模拟费用流,会不了一点口牙。
考虑如下费用流模型:
- \((S,a_i,1,a_i)\),\((b_i,T,1,b_i)\);
- \((a_i,b_i,1,0)\),表示选择这一对相同的下标;
- \((U,V,K-L,0)\),\((a_i,U,1,0)\),\((V,b_i,1,0)\),表示其它的下标最多选 \(K_L\) 个。
考虑模拟费用流,每一轮增广会选择两个未选择的点 \(a_i,b_j\) 改为选择,过程中有如下 \(5\) 种情况:
- \(i=j\),即选择某一对 \((a_i,b_i)\);
- \(i=\neq j\),即选择某一对 \((a_i,b_j),i\neq j\),耗费一单位自由流量;
- 将 \(a_i\) 与 \(b_i\),\(a_j\) 与 \(b_j\) 配对,要求 \(b_i,a_j\) 都已经配对,增加一单位自由流量;
- 将 \(a_i\) 与 \(b_i\) 配对,\(b_j\) 与 \(b_i\) 原来配对的 \(a_k\) 配对。
- 将 \(b_j\) 与 \(a_j\) 配对,\(a_i\) 与 \(a_j\) 原来配对的 \(b_k\) 配对。
于是使用五个堆维护:两侧的 \(\max\),\(a+b\) 的 \(\max\),本侧未选另一侧选的 \(\max\)。
Day2T1
显然这就是一个最短路问题。考虑做一个 dijkstra,优先队列里面存放当前所有的边(点到矩形),每次取出一条边,就更新矩形内所有还没有更新过的点的最短路,并把它们删掉。查询点可以用线段树套 set 维护,复杂度 \(O(n\log^2n)\)。
Day2T2
大胆猜测结论:一次函数洗牌后的期望还是一次函数,二次函数洗牌后的期望还是二次函数。然后只用递推前三项就可以了。
NOI2018
Day1T1
考虑一个最高的水位线使所有点通过没有积水的边就可以连通,也就是求出了一棵海拔的最大生成树。会发现只有这棵树上的边是可能开车经过的。建出 Kruskal 重构树,则固定水位时,每个点开车能到达的点是重构树的一棵子树。预处理出每个点走到 \(1\) 号点的最短路,然后查询子树最小值即可。至于确定这棵子树只需要倍增。
Day1T2
容易发现好排列等价于最长下降子序列不超过 \(2\)。考虑 DP,设 \(f_{i,j}\) 表示前 \(i\) 个数的最大值为 \(j\),此后的方案数。下一个数如果填大于 \(j\) 的数,方案数为 \(\sum_{k=j+1}^n f_{i+1,k}\);如果填小于 \(j\) 的数,就只能填最小值,方案数为 \(f_{i+1,j}\),当然在 \(i=j\) 的时候没有这种转移,可以认为 \(f_{i,\lt i}=0\)。因此,对所有 \(i\le j\),\(f_{i,j}=\sum_{k=j}^{n}f_{i+1,k}\)。
考虑这个东西的组合意义:\(f_{i,j}\) 表示从 \((i,j)\) 开始,每一步可以走到下一列的更高处,不跨过 \(y=x\),到达 \((n,n)\) 的方案数。 也就等价于每次向右向上走一个单位长度,走到 \((n,n)\) 的方案数。这是卡特兰数的经典模型。利用相同的证明方式,可以求出 \(f_{i,j}=C_{2n-i-j-1}^{n-i-1}-C_{2n-i-j-1}^{n-j-2}\)。
最后来考虑字典序的限制。来求前 \(i-1\) 位都相同,第 \(i\) 位更大的方案数。设此前的最大值为 \(mx\),未出现的最小值为 \(mn\),当前 \(q_i=v\)。容易发现方案数应该是 \(\sum_{k=\max(v,mx)+1}^{n}f_{i,k}=f_{i-1,\max(v,mx)+1}\)。但是当 \(v\in(mn,mx)\) 时,此后不再有合法方案。
时间复杂度 \(O(n)\)。
Day1T3
一个简单的问题是如何求 \(S\) 和 \(T\) 的公共子串数目。只要把 \(T\) 放在 \(S\) 的 SAM 和 \(T\) 的 SAM 上同时匹配即可。这里加一个线段树,判断是否有 endpos 在区间内即可。注意要暴力地减少长度而不是直接跳 slink,但是数据好像很水怎么写都能过。
Day2T1
用一个 multiset 模拟容易求出每一次选择的攻击力 \(k_i\)。然后问题等价于求最小的 \(x\),使得 \(p_i\mid a_i-k_ix\),且 \(a_i-k_ix\le 0\)。使用 exCRT 求解即可。
NOI2017
Day1T1
显然此题应当使用数据结构维护二进制位。那么直接使用 std::set
去维护所有极长的连续段。修改时把每个数拆成 \(\le 30\) 个二进制位依次修改,根据加减讨论一下即可。该做法需要大力卡常。不过现在CCF的机子已经很快了来着?
Day1T2
直接哈希就可以了。用链表维护,连接两个序列的时候计算交界处新产生的串的哈希值,插入哈希表里。断开两个序列时同理在哈希表中减掉。因为断开操作很少,每一次断开最多增加 \(O(k^2)\) 个串串,而初始是 \(O(nk)\) 个串,所以总的串串数目是 \(O(nk+ck^2)\) 的,总时间复杂度就是 \(O(nk+ck^2+\sum |s|)\),哈希表的常数忽略不计。
Day1T3
来求最大矩形面积不超过 \(s\) 的概率。因为选择的矩形一定要贴紧下边界,所以只需要关心每一列底部合法的高度。进一步,一个高度为 \(h\) 的矩形是由连续一段高度 \(\ge h\) 的部分构成的。那就可以设计 DP 状态:\(dp_{i,j}\) 表示已经有一段长度为 \(j\),高度 \(\ge i\) 的极长连续段,这部分合法的概率。转移枚举第一个高度等于 \(j\) 的位置 \(k\),有 \(dp_{i+1,k}dp_{i,j-k-1}p^{k}(1-p)\to dp_{i,j}\)。这个 \(k\) 也可能不存在,有 \(dp_{i+1,j}p^j\to dp_{i,j}\)。直接这样做的时间复杂度是 \(O(s^2\log s+ns)\) 的,因为合法的状态只有 \(ij\le s\),所以只有 \(j=0\) 的部分复杂度高。而 \(j=0\) 的部分是一个常系数齐次线性递推,可以用多项式取模进行优化。总的时间复杂度 \(O(s(s+\log n)\log s)\)。
Day2T1
“用车规则”的限制明示这道题应该使用 2-SAT 做。如果没有适合所有赛车的地图,那么直接 2-SAT 就做完了。而适合所有赛车的地图数量很少,所以可以枚举,将所有 \(x\) 换成 \(a\) 或 \(b\)。容易发现这样不会漏解。时间复杂度 \(O(2^dn)\)。
Day2T2
好神仙一题。
首先考虑“时间倒流”,每天会加入一些菜,菜可以无限期保存。这样“变质”就丢掉了。
然后建立费用流模型:
- 为第 \(t\) 天的第 \(i\) 种蔬菜建立点 \((t,i)\),连边 \(S\to(t,i)\),边权为 \(a_i\),容量为当天加入的菜的数目。
- 连边 \((t,i)\to (t+1,i)\),表示可以将蔬菜留到下一天。
- 为第 \(t\) 天建立一个点 \([t]\) 表示出售,连边 \((t,i)\to[t]\),连边 \([t]\to T\),容量为 \(m\)。
- 在菜 \(i\) 第一次出现的时间,从 \(S\to (t,i)\) 中分出一个流量,边权改为 \(a_i+s_i\)。
然后求解最大费用任意流。
接着通过模拟费用流找到反悔贪心。考虑从后向前依次加入每一天的出售点,观察会新形成怎样的“源汇路”或“环”。
结论是 \(S\) 的出边不会退流。也就是说,在未“时光倒流”的原问题中,每一天售卖蔬菜的最优方案包含上一天的最优方案。于是只用求出 \(\max p\) 天的最优方案,保留其中最贵的 \(p_jm\) 棵就得到 \(p_j\) 天的最优方案。
至于如何求出前 \(\max p\) 天的最优方案,回到费用流模型。这次可以换一个顺序处理:每次加入一天的所有蔬菜和出售点。可能的决策有两种:卖掉今天到达的某个菜,或者卖掉之前存下来的某个菜。于是拿一个堆维护一下所有菜的集合即可。
还是好抽象啊
Day2T3
Link.
谁家好人 NOI 出计算几何啊