P9481 [NOI2023] 贸易

· · 个人记录

不好评价题。

容易知道在该题目条件下 dis[u\to v]=dis[u\to\text{LCA}(u,v)]+dis[\text{LCA}(u,v)\to v]。其中 dis[u\to\text{LCA}(u,v)]u 一直往父亲跳,容易预处理。现在难点在于处理出所有 dis[\text{LCA}(u,v)\to v]。换言之,是处理出所有 dis[u \to v],其中 v\in subtree_u

有很多做法,这里写两种。

做法 1:

所有满足 uv 的祖先的 (u,v) 点对数量(即要处理的点对数)为 \sum_{i=0}^{n-1}2^i\times(2^{n-i+1}-1)=O(n2^n)

考虑 u\to v 的最段路长什么样。一定是 u 先向上走(也可以不走)到一个祖先 anc_u,然后由 anc_u 经过一条第二类边向下走,然后经过一些不知道什么路径到达 v。其实就是 不经过 anc_u 的祖先走一条到 v 的最短路

由此,我们可以预处理出 d[u][v] (u\in anc_v) 表示从 u 开始 不经过 u 的祖先到 v 的最短路。易得此时经过的点全都是 u 子树内的点,直接从 u 开始向子树内跑一遍单源最短路,限制一下不能走 u\to fa_u 的边就行了。总复杂度 O(n2^n\log2^n)=O(n^22^n)

然后考虑用 d[u][v] 处理出 dis[u][v]。我们按深度从小到大枚举 u 并用 u 的祖先更新每个 dis[u][v]。相当于枚举 (u,v) 后再枚举可能的 anc_u,用 d[anc_u][v]dis[u\to anc_u](后者之前预处理过了)来更新。

做法 2:

枚举每条第二类边,更新其影响。

观察到一条 (u,v) 的第二类边只会影响 1\to v 这条链上的点对(v 子树中的点可以直接向上走),直接 O(n^2) 暴力枚举并更新。

但是如果这样做的话有些点对的更新存在疏漏。观察到 u 深度越浅,其影响的点数越多,故应该按照 u 的深度从浅到深更新。

复杂度 O(mn^2),若视 O(m)=O(2^n) 则为 O(n^22^n)

至此处理完所需的东西,而统计答案则是简单的。枚举 \text{LCA}uv 一定在两棵不同子树中。对每条路径计算它对答案贡献了多少次,加起来即可。