洛谷题单指南-图论之树-P3038 [USACO11DEC] Grass Planting G

原题链接:https://www.luogu.com.cn/problem/P3038

题意解读:两种操作,对树上路径上的所有边权值加1,查询某一条边的值。

解题思路:

重链剖分既可以对点进行维护,也可以对边进行维护,区别在于边比点少一个,如果边u->v,显然边权值应该归到v,因为u不能保证边是唯一。

题目要求对路径上所有边加1,可以通过重链剖分转换成对一系列区间进行加1操作,查询只需对某一条边,因此是一个区间更新、单点查询问题,用树状数组维护一个dfs序的差分数组即可,区间修改转换成差分数组的单点修改,单点查询转换成前缀和查询。

需要注意的是,在对剖分后的重链区间进行修改时,会涵盖u-v路径上所有点,而对于边来说,不应该包含lca(u,v)这个点对应的边,因此要在区间操作是排除掉对lca(u,v)这个点的修改。

重链剖分中,lca很容易求得,u、v不断沿着重链往上跳的过程中,当两个点所在重链顶端重合时,u、v深度较小的节点即lca,修改时跳过lca即可。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;

vector<int> g[N];
int tr[N]; //树状数组,维护树的dfn序节点的值,用u->v的v节点值代表边
int depth[N], fa[N], sz[N], son[N], dfn[N], cnt, rk[N], top[N]; //重链剖分相关数据
int n, m;

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

void add(int x, int val)
{
    for(int i = x ; i <= n; i += lowbit(i)) //一共n-1条边
        tr[i] += val;
}

int sum(int x)
{
    int res = 0;
    for(int i = x; i; i -= lowbit(i))
        res += tr[i];
    return res;
}

void dfs1(int u, int p, int d)
{
    sz[u] = 1;
    depth[u] = d;
    fa[u] = p;
    for(auto v : g[u])
    {
        if(v == p) continue;
        dfs1(v, u, d + 1);
        sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int t)
{
    top[u] = t;
    dfn[u] = ++cnt;
    rk[cnt] = u;
    if(son[u]) dfs2(son[u], t);
    for(auto v : g[u])
    {
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

void updatePath(int u, int v, int val)
{
    while(top[u] != top[v])
    {
        if(depth[top[u]] < depth[top[v]]) swap(u, v);
        add(dfn[top[u]], 1);
        add(dfn[u] + 1, -1);
        u = fa[top[u]];
    }
    if(depth[u] > depth[v]) swap(u, v);
    //u就是LCA,对于路径权值的更新要排除LCA
    add(dfn[u] + 1, 1);
    add(dfn[v] + 1, -1);
}

int queryEdge(int u, int v)
{
    if(depth[u] < depth[v]) swap(u, v);
    return sum(dfn[u]);
}

int main()
{
    cin >> n >> m;
    int u, v;
    for(int i = 1; i < n; i++)
    {
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs1(1, 0, 0);
    dfs2(1, 1);
    char op;
    while(m--)
    {
        cin >> op >> u >> v;
        if(op == 'P') updatePath(u, v, 1);
        else cout << queryEdge(u, v) << endl;
    }
    return 0;
}

 

posted @ 2025-03-10 12:55  hackerchef  阅读(36)  评论(0)    收藏  举报