hdu5692 Snacks (dfs序+线段树)

题意:

百度科技园内有n个零食机,零食机之间通过n−1条路相互连通。每个零食机都有一个值v,表示为小度熊提供零食的价值。

由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。

为小度熊规划一个路线,使得路线上的价值总和最大。
输入数据第一行是一个整数T(T≤10),表示有T组测试数据。

对于每组数据,包含两个整数n,m(1≤n,m≤100000),表示有n个零食机,m次操作。

接下来n−1行,每行两个整数x和y(0≤x,y< n),表示编号为x的零食机与编号为y的零食机相连。

接下来一行由n个数组成,表示从编号为0到编号为n−1的零食机的初始价值v(|v|<100000)。

接下来m行,有两种操作:0 x y,表示编号为x的零食机的价值变为y;1 x,表示询问从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。

分析:

1 x的操作也就是问走到x之后,在x的子树中走哪一支可以获得最大的分数。可以用dis[i]表示从 0 走到点 i 的得分和。然后求出树的dfs序,这样1 x就转化成了求一个区间最值,区间是[l[x], r[x]]。修改操作就相当于是一个区间加,给这个点的子树区间每个点都加上差值。注意对原数组分数的更新,因为在updage中用到了原数组。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
#define sz(x) ((int)x.size())
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 1e5 + 5;
const double EPS = 1e-8;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m, times, l[MAXN], r[MAXN], dfn[MAXN];
vector<int> G[MAXN];
ll tree[MAXN << 2], laz[MAXN << 2], dis[MAXN], c[MAXN];
void pushup(int rt) {
    tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
void pushdown(int rt) {
    if (laz[rt]) {
        tree[rt << 1] += laz[rt];
        tree[rt << 1 | 1] += laz[rt];
        laz[rt << 1] += laz[rt];
        laz[rt << 1 | 1] += laz[rt];
        laz[rt] = 0;
    }
}
void build(int rt, int l, int r) {
    laz[rt] = 0;
    if (l == r) {
        tree[rt] = dis[dfn[l]];
        return ;
    }
    build(lson);
    build(rson);
    pushup(rt);
}
void update(int L, int R, ll v, int rt, int l, int r) {
    if (L <= l && R >= r) {
        tree[rt] += v;
        laz[rt] += v;
        return;
    }
    pushdown(rt);
    if (L <= (l + r) / 2)   update(L, R, v, lson);
    if (R > (l + r) / 2)    update(L, R, v, rson);
    pushup(rt);
}
ll query(int L, int R, int rt, int l, int r) {
    if (L <= l && R >= r) {
        return tree[rt];
    }
    pushdown(rt);
    if (L > (l + r) / 2)    return query(L,R,rson);
    else if (R <= (l + r) / 2)  return query(L,R,lson);
    else return max(query(L,R,lson),query(L,R,rson));
}
void dfs(int u, int pre) {
    dfn[++times] = u;
    l[u] = times;
    for (int i = 0; i < sz(G[u]); i++) {
        int v = G[u][i];
        if (v != pre) {
            dis[v] = dis[u] + c[v];
            dfs(v, u);
        }
    }
    r[u] = times;
}
void init() {
    for (int i = 0; i < MAXN; i++)  G[i].clear();
    ms(dis, 0); ms(l, 0); ms(r, 0); ms(dfn, 0);
    times = 0;
}
int main() {
    ios::sync_with_stdio(false);
    // freopen("1.txt", "r", stdin);
    int Case, u, v, op;
    cin >> Case;
    for (int _ = 1; _ <= Case; _++) {
        cout << "Case #" << _ << ":\n";
        cin >> n >> m;
        init();
        for (int i = 0; i < n - 1; i++) {
            cin >> u >> v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        for (int i = 0; i < n; i++) {
            cin >> c[i];
        }
        dis[0] = c[0];
        dfs(0, -1);
        build(1, 1, n);
        while (m--) {
            cin >> op >> u;
            if (op == 1) {
                cout << query(l[u], r[u], 1, 1, n) << endl;
            }
            else {
                cin >> v;
                update(l[u], r[u], (ll)v - c[u], 1, 1, n);
                c[u] = v;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值