2017年8月10号提高组T3 树

本文介绍了一种处理有根树上点权值更新、子树查询及根节点变化等问题的方法。通过DFS将树转化为序列,并利用线段树进行高效查询与更新操作。适用于数据规模较大情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description


给你一棵大小为n的有根树,每个点有点权,要求完成以下操作:
V x y把点x的权值变成y
E x把有根树的根变为x
Q x查询点x的子树的最小值

Input


第一行两个整数n,m,表示点数和操作数。
接下来n行,每行两个数f,v,第i行的两个数表示i的父亲和i的权值,且保证f接下来m行,每行表示一个操作。

Output


对于每个Q操作,输出一个整数表示最小值。

Hint


对于30%的数据,n<=1000.
对于100%的数据,n,m<=100000,权值<=10^9

Source


BY BPM

Solution


不考虑修改点权和修改根是很好做的,dfs一轮就可以了。

考虑修改和查询,我们可以通过dfs把树转换成序列,修改、查询的操作就变成序列上的操作了,线段树搞定。

再考虑换根。我们让1恒为根,不难发现如果询问节点x在当前根root的子树内,则x在当前根下的子树仍为x在1位根下的子树;否则,简单画一下图就可以发现,x在当前根下的子树变成了除了以1为根时x的子树外的其余部分,也就是说把dfs序去掉x以1为根时的子树那部分后,其余部分就是x在当前根下的子树。
继续用线段树来做即可。

Code


#include <stdio.h>
#define rep(i, st, ed) for (int i = st; i <= ed; i += 1)
#define erg(i, st) for (int i = ls[st]; i; i = e[i].next)
#define min(x, y) (x)<(y)?(x):(y)
#define max(x, y) (x)>(y)?(x):(y)
#define INF 0x3f3f3f3f
#define N 100001
#define E N
struct edge{int x, y, next;}e[E];
struct treeNode{
    int l, r, mn;
    bool operator <(treeNode b){return (l >= b.l)&&(r < b.r)||(l > b.l)&&(r <= b.r);}
    bool operator <=(treeNode b){return (l >= b.l)&&(r <= b.r);}
}t[N << 2 | 1], rc[N];
int father[N], ls[N], v[N];
int edgeCnt = 0, rCnt = 0;
inline int read(){
    int x = 0; char ch = getchar();
    while (ch < '0' || ch > '9'){ch = getchar();}
    while (ch <= '9' && ch >= '0'){x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
    return x;
}
inline void addEdge(int x, int y){
    e[++ edgeCnt] = (edge){x, y, ls[x]}; ls[x] = edgeCnt;
}
inline void modify(int now, int x, int v){
    if (t[now].l == t[now].r){
        t[now].mn = v;
        return;
    }
    int mid = (t[now].l + t[now].r) >> 1;
    if (x <= mid){modify(now << 1, x, v);}
    if (x > mid){modify(now << 1 | 1, x, v);}
    t[now].mn = min(t[now << 1].mn, t[now << 1 | 1].mn);
}
inline int query(int now, int l, int r){
    if (l > r){return INF;}
    if (t[now].l == l && t[now].r == r){return t[now].mn;}

    int mid = (t[now].l + t[now].r) >> 1;

    if (r <= mid){return query(now << 1, l, r);}
    if (l > mid){return query(now << 1 | 1, l, r);}
    return min(query(now << 1, l, mid), query(now << 1 | 1, mid + 1, r));
}
inline void build(int now, int l, int r){
    t[now] = (treeNode){l, r, 0};
    if (l == r){return ;}
    int mid = (l + r) >> 1;
    build(now << 1, l, mid);
    build(now << 1 | 1, mid + 1, r);
}
inline void dfs(int fa, int now){
    rc[now].mn = rc[now].l = rc[now].r = ++ rCnt;
    modify(1, rc[now].l, v[now]);
    erg(i, now){
        dfs(now, e[i].y);
        rc[now].r = max(rc[now].r, rc[e[i].y].r);
    }
}
int main(void){
    int n = read(), m = read();
    rep(i, 1, n){
        father[i] = read(); v[i] = read();
        addEdge(father[i], i);
    }
    build(1, 1, n);
    int root = 1;
    dfs(0, root);
    rep(i, 1, m){
        char ch = getchar();
        int x = read();
        if (ch == 'V'){
            int y = read();
            modify(1, rc[x].l, y);
        }else if (ch == 'Q'){
            if (x == root){
                printf("%d\n", query(1, 1, rCnt));
            }else if (rc[root] < rc[x]){
                int now;
                erg(i, x){
                    if (rc[root] <= rc[e[i].y]){
                        now = e[i].y;
                        break;
                    }
                }
                printf("%d\n", min(query(1, 1, rc[now].l - 1), query(1, rc[now].r + 1, rCnt)));
            }else{
                printf("%d\n", query(1, rc[x].l, rc[x].r));
            }
        }else if (ch == 'E'){
            root = x;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值