专栏文章

CF860E Arkady and a Nobody-men 题解

CF860E题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min61ob5
此快照首次捕获于
2025/12/01 21:09
3 个月前
此快照最后确认于
2025/12/01 21:09
3 个月前
查看原文
感觉很牛啊,算是一道并不模板的长剖优化 dp 好题了。
先考虑编一个暴力 dp。显然可以设 fuf_u 表示节点 uu 的可忽略性,则有转移
fu=ffau+depfau+wuf_u = f_{\text{fa}_u} + \text{dep}_{\text{fa}_u} + w_u
其中 wuw_u 表示与 uu 的深度相同的点对 uu 的贡献和。
现在考虑怎么求 wuw_u。最暴力的方法是你对于每个节点 uu 都记录一些三元组 (v,depv,wv)(v, \text{dep}_v, w_v),其中的变量分别表示 uu 的子树内存在的一个节点、其深度与已经计算的同层贡献。直接计算是 O(n2)O(n^2) 的。
考虑如何优化。我们把转移 ww 的过程(即对于当前考虑的节点,合并其两棵子树的三元组的过程)写下来,即对于当前节点 cur\text{cur},枚举分别在已经处理过的子树与当前考虑的子树中的同深度三元组 (u,depu,wu)(u, \text{dep}_u, w_u)(v,depv,wv)(v, \text{dep}_v, w_v),有转移
{wudepcurwvdepcur\begin{cases} {w_u \leftarrow \text{dep}_{\text{cur}}} \\ {w_v \leftarrow \text{dep}_{\text{cur}}} \end{cases}
一个观察是对于同一层的所有 uu,当前子树对它们的贡献是相同的(设同层的 vv 的数量为 cnt\text{cnt},则贡献为 depcur×cnt\text{dep}_{\text{cur}} \times \text{cnt}),对 vv 同理。因此考虑对于每个深度只保留一个点,并建立一棵新树表示贡献的传递关系。树中的边 uvalvu \xrightarrow{\text{val}} v 表示 wv=wu+valw_v = w_u + \text{val} 恒成立。此时合并子树的代价就变成了 O(树高)O(树高),长剖优化即可。
具体地,对于节点 uu 及其子树内存在的深度 dd,记录二元组 (p,cntp)(p, \text{cnt}_p),表示 子树中深度为 dd 的点由 dd 代表,同时这样的点实际有 cntp\text{cnt}_p 个。转移即为
{wpdepu×cntvwvdepu×cntp\begin{cases} {w_p \leftarrow \text{dep}_u \times \text{cnt}_v} \\ {w_v \leftarrow \text{dep}_u \times \text{cnt}_p} \end{cases}
同时让 pp 在新树中向 vv 即可。二元组可以用任意你喜欢的方式(如链式前向星或指针)维护。该做法的时空复杂度均为 O(n)O(n)
codeCPP
#include <bits/stdc++.h>
#define ll long long

using namespace std;

constexpr int N = 5e5 + 5;

int n, rt;
int fa[N], dep[N], h[N], son[N];
ll ans[N];
vector<int> e[N];
vector<array<ll, 2>> E[N];
int buf1[N], *p1 = buf1, *p[N];
int buf2[N], *p2 = buf2, *cnt[N];

void dfs1(int u) {
    h[u] = 1;
    dep[u] = dep[fa[u]] + 1;
    for (auto v : e[u]) {
    dfs1(v);
        h[u] = max(h[u], h[v] + 1);
        if (h[v] > h[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int tp) {
    if (u == tp) {
        p[u] = p1;
        p1 += h[u];
        cnt[u] = p2;
        p2 += h[u];
    }
    if (son[u]) {
        p[son[u]] = p[u] + 1;
        cnt[son[u]] = cnt[u] + 1;
        dfs2(son[u], tp);
    }
    p[u][0] = u, cnt[u][0] = 1;
    for (auto v : e[u]) {
        if (v == son[u]) continue;
        dfs2(v, v);
        for (int i = 0; i < h[v]; ++i) {
            ans[p[u][i + 1]] += 1ll * dep[u] * cnt[v][i];
            ans[p[v][i]] += 1ll * dep[u] * cnt[u][i + 1];
            E[p[u][i + 1]].push_back({p[v][i], ans[p[v][i]] - ans[p[u][i + 1]]});
            cnt[u][i + 1] += cnt[v][i];
        }
    }
}

void dfs3(int u, bool flag) {
    if (flag && son[u]) {
        dfs3(son[u], flag);
    }
    for (auto cur : E[u]) {
        ans[cur[0]] = ans[u] + cur[1];
        dfs3(cur[0], false);
    }
}

void dfs4(int u) {
    for (auto v : e[u]) {
        ans[v] += ans[u] + dep[u];
        dfs4(v);
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
    
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> fa[i];
        if (fa[i] > 0) {
            e[fa[i]].push_back(i);
        } else {
            rt = i;
        }
    }
    
    dfs1(rt);
    dfs2(rt, rt);
    dfs3(rt, true);
    dfs4(rt);
    
    for (int i = 1; i <= n; ++i) {
        cout << ans[i] << " \n"[i == n];
    }
    
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...