[POI2014]HOT-Hotels 加强版 解题报告

长链优化 DP 好题。
考虑朴素的 dp,可以先手模几组数据,发现满足要求的三元组类似下图:

因此,假设当前点为 \(u\),当前处理的子节点为 \(v\),与 \(u\) 点距离 \(j\) 的点有 \(f_{u,j}\) 个,\(u\) 的子树中,满足 \(\operatorname{dis}(\operatorname{lca}(x,y),x)=\operatorname{dis}(\operatorname{lca}(x,y),y) = \operatorname{dis}(\operatorname{lca}(x,y),i) + j\)\((x,y)\) 的点对数量。
可以得出如下转移:

g[u][j + 1] += f[v][j] * f[u][j + 1];
g[u][j - 1] += g[v][j];

f[u][j + 1] += f[v][j];

避免算重,答案应该在合并 \(u\)\(v\) 之前求出:

ans += f[u][j - 1] * g[v][j];
ans += f[v][j] * g[u][j + 1];

朴素转移时间、空间复杂度均为 \(\mathcal{O}(n^2)\)
\(n \le 1e5\) 的范围下无法接受,考虑优化。
我们注意到转移只关心距离,而在树上,距离与深度相关,考虑用长链剖分与指针结合均摊复杂度。
注意到 \(f_{u,j+1}\)\(g_{u,j-1}\) 的转移只和 \(f_{v,j},g_{v,j}\) 有关,使用指针均摊这里的空间。
将整棵树长链剖分后,一条长链上的部分可以 \(\mathcal O(len)\) 解决,其中 \(len\) 是长链长度。两条长链之间的合并由于已知深度,也可以 \(\mathcal O(len)\) 解决,因此时间、空间复杂度均摊 \(\mathcal O(n)\),可以通过本题。
代码只给出 dp 部分,f,g 为指针数组,dep_son 为长儿子,cur 为指针,fur 为从长链底到 \(u\) 的距离,可以发现,指针数组节约了大量的无效空间开销。

il void dfs2(int u, int fa) {
    // cerr << u << endl;
    if (dep_son[u]) 
        f[dep_son[u]] = f[u] + 1, g[dep_son[u]] = g[u] - 1, dfs2(dep_son[u], u);
    f[u][0] = 1; ans += g[u][0];
    for (int v : Es[u]) {
        if (v == fa || v == dep_son[u]) continue;

        f[v] = cur; cur += fur[v] << 1; g[v] = cur; cur += fur[v] << 1;
        dfs2(v, u);
        for (int i = 0; i < fur[v]; ++i) {
            if (i) ans += f[u][i - 1] * g[v][i];
            ans += g[u][i + 1] * f[v][i];
        }
        for (int i = 0; i < fur[v]; ++i) {
            g[u][i + 1] += f[u][i + 1] * f[v][i];
            if (i) g[u][i - 1] += g[v][i];
            f[u][i + 1] += f[v][i];
        }
    }
}
posted @ 2023-03-07 10:50  MisterRabbit  阅读(32)  评论(0)    收藏  举报