Snacks dfs 序 + 线段树

传送门

题目描述

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

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

为小度熊规划一个路线,使得路线上的价值总和最大。

分析

这道题我们可以把树形结构去转换成链式结构,去维护从跟节点到每一个节点的距离,然后用线段去去维护区间最值,修改一个点的价值,其实就是对这个点以及这个点的子树进行修改,也就转换成了区间修改,所以我们可以用dfs去把这棵树转换成链,然后用线段树做到区间查询和修改

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
const ll INF = 0x3f3f3f3f3f3f;
const int N = 100010;
int h[N],ne[N * 2],e[N * 2],idx;
ll d[N],s[N];
int in[N],out[N];
int n,m;
int cnt;

struct Node{
    int l,r;
    ll sum,add;
}tr[N << 2];

void add(int x,int y){
    ne[idx] = h[x],e[idx] = y,h[x] = idx++;
}

void dfs(int u,int fa,ll pp){
    in[u] = ++cnt;
    d[cnt] = pp;
    for(int i = h[u];~i;i = ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dfs(j,u,pp + s[j]);
    }
    out[u] = cnt;
}

void pushup(int u){
    tr[u].sum = max(tr[u << 1].sum,tr[u << 1 | 1].sum);
}

void down(int u){
    auto &f = tr[u],&l = tr[u << 1],&r = tr[u << 1 | 1];
    if(f.add){
        l.add += f.add,l.sum += f.add;
        r.add += f.add,r.sum += f.add;
        f.add = 0;
    }
}

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

void modify(int u,int l,int r,ll x){
    if(tr[u].l >= l && tr[u].r <= r){
        tr[u].sum += x;
        tr[u].add += x;
        return;
    }
    down(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if(l <= mid) modify(u << 1,l,r,x);
        if(r > mid) modify(u << 1 | 1,l,r,x);
    pushup(u);
}

ll query(int u,int l,int r){
    if(tr[u].l >= l && tr[u].r <= r){
        return tr[u].sum;
    }
    down(u);
    int mid = tr[u].l + tr[u].r >> 1;
    ll ans = -INF;
    if(l <= mid) ans = query(u << 1,l,r);
    if(r > mid) ans = max(query(u << 1 | 1,l,r),ans);
    return ans;
}


int main(){
    int t;
    scanf("%d",&t);
    for(int ppp = 1;ppp <= t;ppp++){
        memset(h,-1,sizeof h);
        idx = cnt = 0;
        scanf("%d%d",&n,&m);
        printf("Case #%d:\n",ppp);
        for(int i = 1;i < n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y),add(y,x);
        }
        for(int i = 0;i < n;i++) scanf("%lld",&s[i]);
        dfs(0,-1,s[0]);
        build(1,1,n);
        while(m--){
            int op,x,y;
            scanf("%d%d",&op,&x);
            if(!op){
                scanf("%d",&y);
                ll add = 1ll * y - s[x];
                modify(1,in[x],out[x],add);
                s[x] = y;
            }
            else printf("%lld\n",query(1,in[x],out[x]));
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值