2 条题解

  • 0
    @ 2026-8-26 11:12:58

    LCA的贡献 题解

    题目大意

    nn 个节点的树(1 为根),节点 ii 有权值 xix_i。数组 aa1n1\sim n 的排列。对每个区间 [l,r][l,r],若 al,,ara_l,\dots,a_r 这些节点的 LCA 是 kk,则该区间贡献 xkx_k。求所有区间贡献之和。

    算法思路

    转化为:对每个节点 kk,统计"LCA 恰好为 kk"的区间个数,再乘 xkx_k 求和。

    DSU on tree处理。对每个节点,维护其子树内节点在数组 aa 中出现的所有位置形成的若干连续段:

    • SxS_xxx 子树内节点在 aa 中的位置集合。区间 [l,r][l,r] 的 LCA 为 xx,当且仅当 [l,r][l,r] 内的位置都属于 SxS_x,且不全部落在 xx 的某一个儿子的 SS 内。
    • 因为只关心每一个连续段的左右端点,直接维护nn个线段的左右端点,每加入一个位置,若它与左右已有的连续段相接则合并,并用 (len)(len+1)2\frac{(len)(len+1)}2 的增量更新"LCA 恰好为 xx"的区间数。

    先对重儿子保留结果,轻儿子暴力加/删,整体复杂度 O(nlogn)O(n\log n)

    复杂度

    O(nlogn)O(n\log n)

    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    
    const int MAXN = 200010;
    int n;
    vector<int> g[MAXN];
    int pos[MAXN];              // pos[node] = node 在数组 a 中的位置
    ll w[MAXN];
    int sz[MAXN], heavy[MAXN];
    
    int segL[MAXN], segR[MAXN], mark[MAXN];
    int ver[MAXN], curVer = 0;
    ll cnt[MAXN];
    ll total = 0;
    
    ll tri(ll x) { return x * (x + 1) / 2; }
    
    void addPos(int p, int u) {
        mark[p] = curVer;
        segL[p] = segR[p] = p;
        if (mark[p + 1] != curVer) segL[p + 1] = segR[p + 1] = 0;
        if (mark[p - 1] != curVer) segL[p - 1] = segR[p - 1] = 0;
        ll a = 0, b = 0;
        if (segL[p - 1] && segR[p + 1]) {
            a = p - segL[p - 1];
            b = segR[p + 1] - p;
            segR[segL[p - 1]] = segR[p + 1];
            segL[segR[p + 1]] = segL[p - 1];
        } else if (segL[p - 1]) {
            a = p - segL[p - 1];
            segR[segL[p - 1]] = p;
            segL[p] = segL[p - 1];
        } else if (segR[p + 1]) {
            b = segR[p + 1] - p;
            segL[segR[p + 1]] = p;
            segR[p] = segR[p + 1];
        }
        cnt[u] = cnt[u] - tri(a) - tri(b) + tri(a + b + 1);
    }
    
    void dfsSize(int u) {
        sz[u] = 1;
        for (int v : g[u]) {
            dfsSize(v);
            sz[u] += sz[v];
            if (sz[v] > sz[heavy[u]]) heavy[u] = v;
        }
    }
    
    void dfsAdd(int u, int target) {
        addPos(pos[u], target);
        for (int v : g[u]) dfsAdd(v, target);
    }
    
    void dfsClear(int u) {
        mark[pos[u]] = 0;
        segL[pos[u]] = segR[pos[u]] = 0;
        for (int v : g[u]) dfsClear(v);
    }
    
    void dfsDsu(int u) {
        ver[pos[u]] = ++curVer;
        for (int v : g[u]) {
            if (v == heavy[u]) continue;
            dfsDsu(v);
            dfsClear(v);
        }
        if (heavy[u]) {
            dfsDsu(heavy[u]);
            cnt[u] = cnt[heavy[u]];
            curVer = ver[pos[u]] = ver[pos[heavy[u]]];
            for (int v : g[u]) {
                if (v != heavy[u]) dfsAdd(v, u);
            }
        }
        curVer = ver[pos[u]];
        addPos(pos[u], u);
    }
    
    void dfsAnswer(int u) {
        ll k = cnt[u];
        for (int v : g[u]) {
            k -= cnt[v];
            dfsAnswer(v);
        }
        total += k * w[u];
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cin >> n;
        for (int i = 2; i <= n; ++i) {
            int p;
            cin >> p;
            g[p].push_back(i);
        }
        for (int i = 1; i <= n; ++i) {
            int x;
            cin >> x;
            pos[x] = i;
        }
        for (int i = 1; i <= n; ++i) cin >> w[i];
    
        dfsSize(1);
        dfsDsu(1);
        dfsAnswer(1);
        cout << total << '\n';
        return 0;
    }
    

    信息

    ID
    103
    时间
    1000ms
    内存
    512MiB
    难度
    9
    标签
    (无)
    递交数
    24
    已通过
    4
    上传者