hdu5692 Snacks dfs序+线段树

题目传送门

题目大意:给出一颗树,根节点是0,有两种操作,一是修改某个节点的value,二是查询,从根节点出发,经过 x 节点的路径的最大值。

思路:用树状数组写发现还是有些麻烦,最后用线段树了。

   其实这道题的查询,就是查询从根节点到x节点+x节点走下去的路径的最大值,这样会发现,其实就是查询包括x节点的所有子树中权值最大的那个,而包括x节点的子树,如果用dfs序转换一下的话,可以在线段上用一段连续的点表示出来,所以最后就转换成了线段树区间求最大值,然后单点修改的题目了。

  要注意的是dfs序和原标号的对应,有一个地方弄反了,卡了好久。

#pragma comment(linker, "/STACK:1024000000,1024000000") 
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#define CLR(a,b) memset(a,b,sizeof(a))
#define PI acos(-1)
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=100010;
struct edge{
    int to,Next;
}e[maxn*2];
int tot,m,n,head[maxn],pos,dfn[maxn],fa[maxn],son[maxn],l[maxn],r[maxn];
ll val[maxn],dis[maxn];
inline void init(){
    CLR(head,-1),tot=0,pos=0;
    CLR(dis,0);
    fa[1]=1;
}
inline void addv(int u,int v){
    e[++tot]={v,head[u]};
    head[u]=tot;
}
ll tree[maxn << 2], laz[maxn << 2];
inline void pushup(int rt) {
    tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
inline 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;
    }
}
inline void build(int rt, int l, int r) {
    laz[rt] = 0;
    if (l == r) {
        tree[rt] = dis[l];
        return ;
    }
    build(lson);
    build(rson);
    pushup(rt);
}
inline 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);
}
inline 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));
}

inline void dfs(int u,int pre){
    dfn[u]=++pos;
    l[u]=pos;
    son[u]=1;
    dis[dfn[u]]=dis[dfn[pre]]+val[u];
    for(int i=head[u];i!=-1;i=e[i].Next)
    {
        int v=e[i].to;
        if(v==fa[u])continue;
        fa[v]=u;
        
        dfs(v,u);
        son[u]+=son[v];
    }
    r[u]=pos;
}
int main(){
    int T,cas=1;
    cin>>T;
    while(T--){
        init();
        scanf("%d%d",&n,&m);
        
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            addv(u+1,v+1);
            addv(v+1,u+1);
        }
        
        for(int i=1;i<=n;i++)scanf("%lld",&val[i]);
        dfs(1,1);
        build(1,1,n);
        printf("Case #%d:\n",cas++);
        while(m--){
            int op,x;ll y;
            scanf("%d%d",&op,&x);
            if(op==1){
            //    printf("debug  %d  %d\n",dfn[x+1],dfn[x+1]+son[x+1]-1);
            
                printf("%lld\n",query(l[x+1],r[x+1],1,1,n));
            }else{
                scanf("%lld",&y);
                update(l[x+1],r[x+1],y-val[x+1],1,1,n);
                val[x+1]=y;
            }
        }
        
    }
}

Snacks

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 5563    Accepted Submission(s): 1265


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

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

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

 

Input
输入数据第一行是一个整数 T(T10),表示有T组测试数据。

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

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

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

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

本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:

`#pragma comment(linker, "/STACK:1024000000,1024000000") `
 

 

Output
对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。

对于每次询问,输出从编号为0的零食机出发,必须经过编号为 x零食机的路线中,价值总和的最大值。
 

 

Sample Input
1 6 5 0 1 1 2 0 3 3 4 5 3 7 -5 100 20 -5 -7 1 1 1 3 0 2 -1 1 1 1 5
 

 

Sample Output
Case #1: 102 27 2 20

转载于:https://www.cnblogs.com/mountaink/p/9863097.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值