2025“钉耙编程”中国大学生算法设计暑期联赛(2)1009 苹果树 题解记录

大致题意:给定一棵树,点有权值。现在需要维护两种操作:1,a,b1,a,b1,a,b查询树上aaabbb间简单路径上最大的点权;2,a,b2,a,b2,a,b针对树上所有与点aaa相邻的点,使其点权+b+b+b,不包括aaa本身。
解:
考虑用重链剖分,此时操作111很好查询,用线段树维护DFSDFSDFS序即可。
而对于操作222,本来我想着针对aaa的所有儿子,每次跳即可。

                int cnt = (u == 1 ? g[u].size() : g[u].size() - 1);
                int now = 0;
                int node = hl.getSon(u);
                int lim = hl.getDepth(node);

                while (now < cnt) {
                    if (hl.getDepth(node) == lim) {
                        t.change(hl.getDfn(node), hl.getDfn(node), v);
                        d[hl.getDfn(node)] += v;
                        now++;
                    }
                    node = hl.getRnk(hl.getBottom(node) + 1);
                }

写完了还没意识到其本质跟遍历g[u]g[u]g[u]没区别,必定超时的。然后就超时了。
从线段树的懒标记可以得到启发:对于uuu的轻儿子,其作为一条新链的toptoptop,我们不用立即去更新,而是给其父节点uuu打上标记tag[u]tag[u]tag[u];而对于uuu的重儿子和其父亲,我们正常更新。在查询的时候,正常跳跃更新答案。但是在到达链顶toptoptop的时候,由于其是新链的链顶,那么其父亲的重儿子必定不是它。我们查询一下其父亲是否存在tagtagtag,如果有tagtagtag的话,那就代表此toptoptop应当被更新过。于是再单独针对链顶更新一次。
当查询的u,vu,vu,v位于同链的时候,我们需要额外注意:假设uuu深度较小,我们需要考虑其是否可能是其父亲的轻儿子,即

if (sp.getParent(u) && sp.getTop(u) == u) {
    ans = max(ans, a[u] + tag[sp.getParent(u)]);
}

代码如下:

#include<bits/stdc++.h>

#define endl '\n'
#define pii pair<int,int>
#define int long long


using namespace std;

const double eps = 1e-10;
const int mod = 1e9 + 7;

int lowbit(int x) {
    return x & (-x);
}

int ONECNT(int x) {
    return __builtin_popcountll(x);
}

int get(int x, int pos) {
    return (x >> pos) & 1;
}

int qp(int a, int b, int p) {
    int res = 1;
    while (b) {
        if (b & 1) {
            res = res % p * (a % p) % p;
        }
        b >>= 1;
        a = a % p * (a % p) % p;
    }
    return res % p;
}

class HeavyLight {
private:
    int n;
    int timer;
    vector<vector<int>> &adj;
    vector<int> fa, dep, son, top, dfn, rnk, siz;

    // 第一次 DFS: 求 fa(x), dep(x), siz(x), son(x)
    int dfs1(int u, int p) {
        fa[u] = p;
        dep[u] = (p ? dep[p] + 1 : 0);
        siz[u] = 1;
        int maxSize = 0;
        for (int v: adj[u]) {
            if (v == p) continue;
            int subsz = dfs1(v, u);
            if (subsz > maxSize) {
                maxSize = subsz;
                son[u] = v;
            }
            siz[u] += subsz;
        }
        return siz[u];
    }

    // 第二次 DFS: 求 top(x), dfn(x), rnk(x)
    void dfs2(int u, int tp) {
        top[u] = tp;
        dfn[u] = ++timer;
        rnk[timer] = u;
        if (son[u] != 0)
            dfs2(son[u], tp);  //优先对重儿子dfs 保证dfs序连续
        for (int v: adj[u]) {
            if (v == fa[u] || v == son[u]) continue;
            dfs2(v, v);
        }
    }

public:
    HeavyLight(int _n, vector<vector<int>> &_adj)
            : n(_n), adj(_adj), timer(0) {
        fa.assign(n + 1, 0);
        dep.assign(n + 1, 0);
        son.assign(n + 1, 0);
        top.assign(n + 1, 0);
        dfn.assign(n + 1, 0);
        rnk.assign(n + 1, 0);
        siz.assign(n + 1, 0);
    }

    // 初始化,root一般为1
    void init(int root = 1) {
        dfs1(root, 0);
        dfs2(root, root);
    }

    // 路径拆分: 从 u 到 v 分成若干链段,op 为区间操作
    template<typename OP>
    void processPath(int u, int v, OP op) {
        while (top[u] != top[v]) {
            if (dep[top[u]] > dep[top[v]]) {
                op(dfn[top[u]], dfn[u]);
                u = fa[top[u]];
            } else {
                op(dfn[top[v]], dfn[v]);
                v = fa[top[v]];
            }
        }
        if (dep[u] > dep[v]) swap(u, v);
        op(dfn[u], dfn[v]);
    }

    // 求最近公共祖先
    int lca(int u, int v) const {
        while (top[u] != top[v]) {
            if (dep[top[u]] > dep[top[v]]) u = fa[top[u]];
            else v = fa[top[v]];
        }
        return dep[u] < dep[v] ? u : v;
    }

    // 访问器
    int getDfn(int x) const { return dfn[x]; }

    int getRnk(int i) const { return rnk[i]; }

    int getTop(int x) const { return top[x]; }

    int getParent(int x) const { return fa[x]; }

    int getDepth(int x) const { return dep[x]; }

    int getSubtreeSize(int x) const { return siz[x]; }

    int getBottom(int x) const { return getDfn(x) + getSubtreeSize(x) - 1; }

    int getSon(int x) const { return son[x]; }
};

// 用法示例:
// int main() {
//     int n; cin >> n;
//     vector<vector<int>> g(n+1);
//     for (int i = 1; i < n; i++) {
//         int u, v; cin >> u >> v;
//         g[u].push_back(v);
//         g[v].push_back(u);
//     }
//     HeavyLight hld(n, g);
//     hld.init(1);
//     auto op = [&](int l, int r) {
//         // seg.query(l, r);
//     };
//     hld.processPath(u, v, op);
//     return 0;
// }

class tree {  //支持区间修改 区间最值查询
private:
    int n;
    vector<int> t, lazy;

    void build(int x, int l, int r, const vector<int> &arr) {
        if (l == r) {
            t[x] = arr[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(x << 1, l, mid, arr);
        build(x << 1 | 1, mid + 1, r, arr);
        t[x] = max(t[x << 1], t[x << 1 | 1]);
    }

    void pd(int x, int l, int r) {
        if (lazy[x]) {
            lazy[x << 1] += lazy[x];
            lazy[x << 1 | 1] += lazy[x];
            int mid = (l + r) >> 1;
            t[x << 1] += lazy[x];
            t[x << 1 | 1] += lazy[x];
            lazy[x] = 0;
        }
    }

    void change(int x, int l, int r, int ll, int rr, int val) {
        if (ll <= l && rr >= r) {
            lazy[x] += val;
            t[x] += val;
            return;
        }
        if (rr < l || ll > r) return;
        pd(x, l, r);
        int mid = (l + r) >> 1;
        change(x << 1, l, mid, ll, rr, val);
        change(x << 1 | 1, mid + 1, r, ll, rr, val);
        t[x] = max(t[x << 1], t[x << 1 | 1]);
    }

    int que(int x, int l, int r, int ll, int rr) {
        if (ll <= l && rr >= r) {
            return t[x];
        }
        if (ll > r || rr < l) return LLONG_MIN;
        pd(x, l, r);
        int mid = (l + r) >> 1;
        return max(que(x << 1, l, mid, ll, rr), que(x << 1 | 1, mid + 1, r, ll, rr));
    }

public:
    tree(int size) {
        n = size;
        t.resize(size * 4);
        lazy.resize(size * 4);
    }

    void build(const vector<int> &arr) {
        build(1, 1, n, arr);
    }

    void change(int l, int r, int val) {
        change(1, 1, n, l, r, val);
    }

    int que(int l, int r) {
        return que(1, 1, n, l, r);
    }

};

void solve() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    vector<vector<int>> g(n + 1);
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    vector<int> tag(n + 1);

    HeavyLight sp(n, g);

    sp.init(1);

    tree t(n);

    vector<int> d(n + 1);

    for (int i = 1; i <= n; i++) {
        d[sp.getDfn(i)] = a[i];
    }

    t.build(d);

    while (q--) {
        int x, u, v;
        cin >> x >> u >> v;

        if (x == 1) {
            int ans = 0;
            while (sp.getTop(u) != sp.getTop(v)) {
                if (sp.getDepth(sp.getTop(u)) < sp.getDepth(sp.getTop(v))) {
                    swap(u, v);
                }
                if (sp.getDfn(sp.getTop(u)) + 1 <= sp.getDfn(u)) {
                    ans = max(ans, t.que(sp.getDfn(sp.getTop(u)) + 1, sp.getDfn(u)));
                }
                int hd = sp.getTop(u);
                if (sp.getParent(hd)) {
                    ans = max(ans, a[hd] + tag[sp.getParent(hd)]);
                }
                u = sp.getParent(hd);

            }
            if (sp.getDfn(u) > sp.getDfn(v))
                swap(u, v);

            if (sp.getParent(u) && sp.getTop(u) == u) {
                    ans = max(ans, a[u] + tag[sp.getParent(u)]);
            }

            ans = max(ans, t.que(sp.getDfn(u), sp.getDfn(v)));


            cout << ans << endl;
        } else {
            int hvson = sp.getSon(u);
            if (hvson) {
                t.change(sp.getDfn(hvson), sp.getDfn(hvson), v);
                d[sp.getDfn(hvson)] += v;
                a[hvson] += v;
            }
            int fa = sp.getParent(u);
            if (fa) {
                t.change(sp.getDfn(fa), sp.getDfn(fa), v);
                d[sp.getDfn(fa)] += v;
                a[fa] += v;
            }
            tag[u] += v;
        }


    }


}


signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值