HDU5692[Snacks]--DFS序+线段树

【链接】
HDU5962

【题目大意】
给定你一颗n个节点树,有m个操作,操作有两种:0 x y表示将x节点的点权修改成y,1 x表示询问从节点0开始中途经过节点x的路线中点权加和最大的值。

【解题报告】
从题目中可以确定根节点是0。对于每次修改,其实需要修改的只有经过以给定的x为根的子树的节点的路线的值,直接修改复杂度太大,那该如何修改呢?我们可以通过DFS序,将树转化为区间,这样修改就转化为区间修改了,直接用线段树维护0到每个节点路线的值就好了。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=100005,maxm=200005,maxv=400005;
int T,n,m,tot,Time,id[maxn],in[maxn],out[maxn],lnk[maxn],son[maxm],nxt[maxm],w[maxn];
LL dep[maxn];
struct Node
{ 
    int l[maxv],r[maxv];
    LL MAX[maxv],Save[maxv];
    void Pushup(int d){MAX[d]=max(MAX[d<<1],MAX[(d<<1)+1]);}
    void Pushdown(int d)
    {
        LL p=Save[d];
        Save[d<<1]+=p; Save[(d<<1)+1]+=p;
        MAX[d<<1]+=p; MAX[(d<<1)+1]+=p; Save[d]=0;
    }
    void Build(int L,int R,int d)
    {
        l[d]=L; r[d]=R;
        if (L==R) {MAX[d]=dep[id[L]]; return;}
        int mid=(R-L>>1)+L;
        Build(L,mid,d<<1); Build(mid+1,R,(d<<1)+1);
        Pushup(d);
    }
    void Insert(int x,int y,int d,int p)
    {
        int L=l[d],R=r[d];
        if (x==L&&y==R) {Save[d]+=p; MAX[d]+=p;return;}
        Pushdown(d);
        int mid=(R-L>>1)+L;
        if (y<=mid) Insert(x,y,d<<1,p);
         else if (x>mid) Insert(x,y,(d<<1)+1,p);
          else Insert(x,mid,d<<1,p),Insert(mid+1,y,(d<<1)+1,p);
        Pushup(d);
    }
    LL Query(int x,int y,int d)
    {
        int L=l[d],R=r[d];
        if (x==L&&y==R) return MAX[d];
        Pushdown(d);
        int mid=(R-L>>1)+L;
        if (y<=mid) Query(x,y,d<<1);
         else if (x>mid) Query(x,y,(d<<1)+1);
          else return max(Query(x,mid,d<<1),Query(mid+1,y,(d<<1)+1));
    }
}tr;//线段树
inline int Read()
{
    int res=0,f=1;
    char ch=getchar(),cc=ch;
    while (ch<'0'||ch>'9') cc=ch,ch=getchar();
    if (cc=='-') f=-1;
    while (ch>='0'&&ch<='9') res=res*10+ch-48,ch=getchar();
    return res*f;
}
void Add(int x,int y)
{
    son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;
}
void Dfs(int x,int fa)//DFS序
{
    in[x]=++Time; id[Time]=x; dep[x]=dep[fa]+w[x];
    for (int j=lnk[x]; j>=0; j=nxt[j])
     if (son[j]!=fa) Dfs(son[j],x);
    out[x]=Time;
}
void Work(int d)
{
    printf("Case #%d:\n",d);
    n=Read(); m=Read(); tot=Time=0;
    memset(lnk,255,sizeof(lnk));
    memset(dep,0,sizeof(dep));
    memset(tr.Save,0,sizeof(tr.Save));
    for (int i=1,x,y; i<n; i++) x=Read(),y=Read(),Add(x,y),Add(y,x);
    for (int i=0; i<n; i++) w[i]=Read();
    Dfs(0,n); tr.Build(1,n,1); 
    for (int i=1; i<=m; i++)
    {
        int z=Read(),x=Read(),p;
        if (z==1) printf("%lld\n",tr.Query(in[x],out[x],1));
         else p=Read(),tr.Insert(in[x],out[x],1,p-w[x]),w[x]=p;
    }
} 
int main()
{
    freopen("5692.in","r",stdin);
    freopen("5692.out","w",stdout);
    T=Read();
    for (int i=1; i<=T; i++) Work(i);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值