hihoCoder1576 子树中的最小权值 dfs序+线段树

题目:

http://hihocoder.com/problemset/problem/1576?sid=1197257

题意:

描述
给定一棵N个节点的树,编号1~N。其中1号节点是根,并且第i个节点的权值是Vi。
针对这棵树,小Hi会询问小Ho一系列问题。每次小Hi会指定一个节点x,询问小Ho以x为根的子树中,最小的权值是多少。为了增加难度,小Hi可能随时改变其中每个节点的权值。
你能帮助小Ho准确、快速的回答小Hi的问题吗?

输入
第一行一个正整数N。
第二行N个整数,V1, V2, … VN。
第三行n-1个正整数,第i个数Pi表示第i+1号节点的父结点是第Pi号节点。注意1号节点是根。
第四行一个正整数Q,表示有Q个询问/修改权值。
接下来Q行,每行可能有如下两种输入格式:
1 x u
2 x
第一种表示将第x号节点的权值修改为u
第二种表示询问以第x号节点为根的子树中,最小的权值是多少。
对于30%的数据,1 ≤ N, Q ≤ 1000
对于100%的数据,1 ≤ N, Q ≤ 100000, -109 <= Vi, u <= 109

输出
对于每次询问,输出一个整数表示答案。

思路:

首先用dfs标号,然后线段树维护即可

#include <bits/stdc++.h>

using namespace std;

const int N = 100000 + 10, INF = 0x3f3f3f3f;

struct edge
{
    int to, next;
}g[N*2];
struct node
{
    int l, r, val;
}tr[N*4];

int cnt, head[N];
int tot;
int in[N], out[N];
int a[N], b[N];

void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
    tot = 0;
}
void add_edge(int v, int u)
{
    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;
}
void dfs(int v, int fa)
{
    in[v] = ++tot;
    for(int i = head[v]; ~i; i = g[i].next)
    {
        int u = g[i].to;
        if(u == fa) continue;
        dfs(u, v);
    }
    out[v] = tot;
}
void push_up(int k)
{
    tr[k].val = min(tr[k<<1].val, tr[k<<1|1].val);
}
void build(int l, int r, int k)
{
    tr[k].l = l,  tr[k].r = r;
    if(l == r)
    {
        tr[k].val = b[l]; return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, k<<1);
    build(mid+1, r, k<<1|1);
    push_up(k);
}
void update(int x, int val, int k)
{
    if(tr[k].l == tr[k].r && tr[k].l == x)
    {
        tr[k].val = val; return;
    }
    int mid = (tr[k].l + tr[k].r) >> 1;
    if(x <= mid) update(x, val, k<<1);
    else update(x, val, k<<1|1);
    push_up(k);
}
int query(int l, int r, int k)
{
    if(l <= tr[k].l && tr[k].r <= r)
    {
        return tr[k].val;
    }
    int mid = (tr[k].l + tr[k].r) >> 1;
    int ans = INF;
    if(l <= mid) ans = min(ans, query(l, r, k<<1));
    if(r > mid) ans = min(ans, query(l, r, k<<1|1));
    return ans;
}
int main()
{
    init();
    int n, m, opt, x, y;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= n-1; i++)
    {
        scanf("%d", &x);
        add_edge(x, i+1);
    }
    dfs(1, 0);
    for(int i = 1; i <= n; i++) b[in[i]] = a[i];
    build(1, n, 1);
    scanf("%d", &m);
    for(int i = 1; i <= m; i++)
    {
        scanf("%d", &opt);
        if(opt == 1)
        {
            scanf("%d%d", &x, &y);
            update(in[x], y, 1);
        }
        else
        {
            scanf("%d", &x);
            printf("%d\n", query(in[x], out[x], 1));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值