洛谷题单指南-图论之树-P4092 [HEOI2016/TJOI2016] 树

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

题意解读:树支持两种操作:1、单点修改,将某个点打标记 2、路径查询,找到某个点最近打过标记的祖先

解题思路:

依然是一个重链剖分+线段树的题目,有两个关键点

1、区间查询是什么?

要知道某个节点最近打过标记的祖先,在重链剖分后的dfs序中,就是要在节点到根节点之间找一个打过标记的dfs序最大的节点。

2、线段树维护什么?

根据以上分析,线段树只需维护区间中打过标记的最右边的节点即可,对应树上就是dfs序最大的节点。

100分代码:

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

const int N = 100005;

vector<int> g[N];
struct Node
{
    int l, r;
    int maxtag; //区间最右边一个打了标记的节点
} tr[N * 4];
int son[N], siz[N], fa[N], depth[N], top[N], dfn[N], rk[N], cnt;
int n, m;

void dfs1(int u, int p, int d)
{
    depth[u] = d;
    fa[u] = p;
    siz[u] = 1;
    for(auto v : g[u])
    {
        if(v == p) continue;
        dfs1(v, u, d + 1);
        siz[u] += siz[v];
        if(siz[v] > siz[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 pushup(int u)
{
    tr[u].maxtag = max(tr[u << 1].maxtag, tr[u << 1 | 1].maxtag);
}

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if(l == r) tr[u].maxtag = 0;
    else
    {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

//将pos位置标记修改为true
void update(int u, int pos)
{
    if(tr[u].l == tr[u].r) tr[u].maxtag = tr[u].l;
    else 
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(pos <= mid) update(u << 1, pos);
        else update(u << 1 | 1, pos);
        pushup(u);
    }
}

//找区间l~r最右边的tag=true的节点
int query(int u, int l, int r)
{
    if(tr[u].l >= l && tr[u].r <= r) return tr[u].maxtag;
    else if(tr[u].l > r || tr[u].r < l) return 0;
    return max(query(u << 1, l, r), query(u << 1 | 1, l, r));
}

//找到u~1路径上打标记的点中dfn序最大的点
int queryPath(int u)
{
    while(top[u])
    {
        int res = query(1, dfn[top[u]], dfn[u]);
        if(res) return res;
        u = fa[top[u]];
    }
    return 0;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs1(1, 0, 1);
    dfs2(1, 1);
    build(1, 1, n);
    update(1, dfn[1]);
    while(m--)
    {
        char op;
        int x;
        cin >> op >> x;
        if(op == 'C') update(1, dfn[x]);
        else if(op == 'Q') cout << rk[queryPath(x)] << endl;
    }
    return 0;
}

 

posted @ 2025-03-26 10:33  hackerchef  阅读(16)  评论(0)    收藏  举报