[EDISOI] Round 1 题解

写在前面的话

本场比赛难度估计大约可能是 \(\text{NOIp}\) 难度?

考场得分 \(100+100+100+0=300\) ,封榜时排名 \(\text{rank6}\)

T1

题目描述:

有一张地铁交通网 \(G\)\(G\) 拥有 \(n\) 个站点和 \(m\) 条地铁线路.

\(i\) 条地铁线路 \(P_i\) 会经过交通网上的若干站点,形如 \(P_i=(u_1,u_2,u_3,...,u_{k_i})(k_i>0)\):每两个相邻站点 \(u_j,u_{j+1}(j<k_i)\) 之间存在一段属于线路 \(i\) 的从 \(u_j\) 通向 \(u_{j+1}\) 的单向地铁轨道.保证一条地铁线路不重复经过同一站点.但一个站点可能被若干条地铁线路经过>

一种出行方案具体是这样的:从 \(1\) 号站点出发,选定一条经过 \(1\) 号站点的地铁线路并开始乘坐地铁.沿当前地铁线路乘坐地铁的过程中,可以选择换乘其他任意一条经过当前站点的地铁线路.要求最终到达 \(n\) 号站点.乘坐地铁过程中重复经过某一站点或某段地铁轨道是被允许的.并且我们认为从一号节点出发不算换乘。

现在有 \(q\) 个询问,每一次询问给出 \(a,b,c\) 。如果你的路途经过了 \(x\) 条地铁轨道并且换乘了 \(b\) 次,疲劳值就是 \(ax+by\) 。问在换乘不超过 \(c\) 次的最小疲劳值。

\(1\le n \le 10^5\)\(1\le m \le 10^4\)\(1\le q \le 10^5\)\(\sum k_i \le 3\times 10^5\)
\(0 \leqslant c \leqslant 20\)

思路点拨:

题目描述略难懂。我也不好表述所以使用了原题体面并略加修改。

我们发现 \(ax+by(y \leqslant c)\) 的式子十分的抽象,但是我们看见 \(c\) 其实非常的小,我们不妨枚举式子中的 \(y\) 是多少,那么我们唯一需要考虑的是在固定一个 \(y\) 的情况下,\(x\) 最小可以是多少。所以我们果断设计一个状态 \(dp_{i,j}\) 表示目前在节点 \(i\) ,换乘次数为 \(j\) 的最短路径长度。其中 \(dp_{1,0}=0\)

但是我们如果直接在图上转移会有大问题,因为同一个节点不一定只被一条地铁经过,我们就无法处理换乘的信息了。我们考虑一个图论建模,我们对于每一个节点都建立一个中转节点,每一条地铁线路(我们直接新建一些节点来代表线路)向中转节点连一条有向边 ;地铁中转节点又向每一个经过这个节点的点连有向边。

之后我们的转移这么考虑。现在有一个状态 \(dp_{u,j}\) ,如果存在一条有向边 \((u,v)\) ,那么 \(v\) 我们分三类讨论:

  • 这条边十分平凡:\(dp_{v,j}=dp_{u,j}+1\)

  • 这条边的 \(v\) 是中转节点: \(dp_{v,j+1}=dp_{u,j}\)

  • 这条边的 \(u\) 是中转节点: \(dp_{v,j}=dp_{u,j}\)

我们可以通过上述方式转移,但是我们并不知道转移顺序。注意到转移的时候,每一条边的边权是 \(0,1\) 。我们可以使用 \(\text{01-bfs}\) 进行转移。特别值得注意的是,为了方便,我们应该要将终点设为 \(n\) 的中转节点,不然就要枚举每一个复制出来的 \(n\) 节点十分麻烦。可是每一次进入中转节点都会导致换乘次数加一,所以我们可以将 \(dp_{i,j}\)\(j\) 这一维度开到 \(c+1\) ,统计答案的时候少算一次换乘就可以。

答案的统计十分简单,我们暴力枚举换乘次(贡献就 \(by\))数用 \(dp\) 数组帮我们回答 \(ax\) 这一部分的最小值。

时间复杂度 \(O(c\times (n+\sum k +q))\) ,可以通过本题,就是常数略大罢了。

T2

题目描述

现在有 \(n\) 个节点的完全图,第 \(i\) 个节点的点权为 \(a_i\) 。如果 \(u,v\) 之间存在一条边的话,贡献为 \(a_u \oplus a_v\) 。一颗生成树的价值就是全部边的边权和,希望求出原图全部生成树的价值和。

\(n \leqslant 10^6\)

思路点拨

实际上,对于任何一条边 \((u,v)\) ,在全部生成树中出现次数是一样的。这一点很好理解吧。

全部生成树的边的数量和就是 \(n^{n-2}\times (n-1)\) ,将其均摊到每一条边就是 \(\dfrac{n^{n-2}\times (n-1)}{n\times (n-1)\times \frac{1}{2}}=2\times n^{n-3}\) ,令其为 \(w\) 表示每一条边的出现次数。

我们现在的目的就是求出每一条边的价值和然后乘上 \(w\) 就是答案。价值和十分好算,我们枚举每一个二进制位,对于第 \(i\) 个元素,如果该位上是 \(1\) 就找 \(i+1,i+2,..,n\) 这些元素里哪些该位是 \(0\) ;如果该位上是 \(0\) ,就找 \(i+1,i+2,..,n\) 这些元素里哪些该位是 \(1\) 。数量乘上 \(2^{bit}\)\(bit\) 就看你现在枚举到第几位了。过程可以使用前缀和优化,\(O(n \log W)\)

本题还是比第一题简单了不少。

T3

题目描述

现在有一棵 \(n\) 节点的树,树上的每一个点都有一个颜色,黑色或者白色,一开始都是白色。并且有 \(q\) 次操作,每一次操作会改变一个节点的颜色(黑色变白色,白色变黑色)。

每一次询问之后,你需要回答至少要更改多少个黑色节点的颜色才可以使得黑色节点两两之间的路径并中最小的深度的节点发生改变?

\(n,q \leqslant 2\times 10^5\)

思路点拨

对于一个局面,我们将全部的黑色节点建立一颗虚树,策略十分单一:我们找到虚树的根的儿子节点中黑色节点数量最多的子树,将其保留,别的删掉。不然就会存在两个子树同时存在黑色节点,然后这颗虚树的根又被统计到了,最小深度不会发生改变。并且保留黑色节点最多的子树可以让我们改变颜色的次数尽可能的小。

现在的问题有两个:

  • 如何快速找到虚树的根节点并且动态维护?

  • 如何在快速的时间内找到某个节点儿子子树中黑色节点数量的最大值?

先考虑简答的第一个问题:

我们维护一个集合 \(S\)\(S\) 中的元素按照 \(dfn\) 大小排序。每一次我们找到 \(dfn\) 最小的和 \(dfn\) 最大的求树上公共祖先即可。

再考虑第二个问题:

我们发现,如果我们要便利一个节点的儿子最劣是 \(O(n)\) 的,不论别的你操作的再快,还是一个 \(O(nq)\) 的算法。所以要行得通,就不可以遍历全部的儿子节点。全部节点的儿子节点数量和是 \(n\) 这个级别的,我们考虑一个分治

  • 对于儿子数量小于等于 \(B\) 的节点

我们考虑每一个节点更改颜色的时候只会影响这个节点到根的路径,我们使用树链剖分+\(\text{BIT}\) 去进行树上链上加和单点查询。每一次我们暴力遍历儿子然后统计子树中黑色节点数量的最大值。

  • 对于儿子数量大于 \(B\) 的节点

只有 \(\dfrac{n}{B}\) 个这样的节点。我们对于每一个节点开一个动态开点权值线段树,然后每一次我们更改节点的时候,暴力判断这个节点是是否会对这些儿子节点数量大的节点产生影响(判断是否在子树中是 \(O(1)\) 的,可以使用 \(dfn\) 判断)。然后在动态开点权值线段树上修改。每一次查询的时候可以 \(O(1)\) 取根节点的最大值。

注意:我们所说的动态开点权值线段树是针对于儿子的。所以每一次更改还需要使用树上倍增法找到儿子。这一部分比较抽象,建议看代码。

https://www.luogu.com.cn/paste/mjq1cgu7

posted @ 2023-09-10 08:24  Diavolo-Kuang  阅读(12)  评论(1编辑  收藏  举报