【TJOI2015】旅游

时间限制 : 1000 MS 空间限制 : 256 MB

问题描述

为了提高智商,ZJY准备去往一个新世界去旅游。这个世界的城市布局像一棵树。每两座城市之间只有一条路径可以互达。每座城市都有一种宝石,有一定的价格。ZJY为了赚取最高利益,她会选择从A城市买入再转手卖到B城市。由于ZJY买宝石时经常卖萌,因而凡是ZJY路过的城市,这座城市的宝石价格会上涨。让我们来算算ZJY旅游完之后能够赚取的最大利润。(如a城市宝石价格为v,则ZJY出售价格也为v)

输入格式

第一行输入一个正整数N,表示城市个数。
接下来一行输入N个正整数表示每座城市宝石的最初价格p,每个宝石的初始价格不超过100。
第三行开始连续输入N-1行,每行有两个数字x和y。表示x城市和y城市有一条路径。城市编号从1开始。
下一行输入一个整数Q,表示询问次数。
接下来Q行,每行输入三个正整数a,b,v,表示ZJY从a旅游到b,城市宝石上涨v。
即是询问a—>b(有方向)间路径上的max(Price[j]−Price[i])) 且j到a的距离比i到a大。然后把这条路径上所有点的点权+v。
1≤ N≤50000, 1≤Q ≤50000

输出格式

对于每次询问,输出ZJY可能获得的最大利润,如果亏本则输出0。

样例输入 1

3
1 2 3
1 2
2 3
2
1 2 100
1 3 100

样例输出 1

1
1

样例输入 2

3
1 2 3
1 3
2 3
2
1 2 100
1 3 100

样例输出 2

2
2

题解

在树上路径搞来搞去的,多半都会用到树链剖分。我们想要一段区间后面的数减前面的数最大,只有两种情况:区间最大值减它之前的最小值和区间最小值与它后面的最大值的差的绝对值。这样用线段树维护树链即可。(口头AC很简单,然而代码非常恶心QAQ,于是学习了OBlack大佬的巧妙代码orzorz)
要注意的是,由于旅游路线规定了树链上的先后顺序,因此线段树上应记录两种可能方向的答案,而且沿重链往上跳时也不能交换两点顺序……(区间之间合并时涉及的变量很多,这时用struct就十分方便

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=5e4+5;
const int inf=1e9;
int a,b,c,n,q,rt,val[maxn],maxx[maxn<<2],minn[maxn<<2],lans[maxn<<2],rans[maxn<<2],lazy[maxn<<2];
int tot,fa[maxn],dep[maxn],dfn[maxn],pre[maxn],siz[maxn],son[maxn],top[maxn];
vector<int> G[maxn];
struct node
{
    int tmax,tmin,ans;
    node(){}
    node(int tmax,int tmin,int ans):tmax(tmax),tmin(tmin),ans(ans){}
}init;
char buf[1<<20],*head,*tail;
#define GC (head==tail&&(tail=(head=buf)+fread(buf,1,1000000,stdin),head==tail)?0:*head++)
inline int input()
{
    char t=GC;int x=0;
    while(t<48||t>57) t=GC;
    for(;t>=48&&t<=57;t=GC) x=x*10+t-48;
    return x;
}
void get_siz(int x)
{
    dep[x]=dep[fa[x]]+1,siz[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int y=G[x][i];
        if(y==fa[x]) continue;
        fa[y]=x,get_siz(y),siz[x]+=siz[y];
        if(siz[y]>siz[son[x]]) son[x]=y;
    }
}
void get_way(int x,int anc)
{
    if(!x) return;
    dfn[x]=++tot,pre[tot]=x,top[x]=anc;
    get_way(son[x],anc);
    for(int i=0;i<G[x].size();i++)
    {
        int y=G[x][i];
        if(y==fa[x]||y==son[x]) continue;
        get_way(y,y);
    }
}
node merge(node x,node y)
{
    node temp=init;
    temp.tmax=max(x.tmax,y.tmax),temp.tmin=min(x.tmin,y.tmin);
    temp.ans=max(y.tmax-x.tmin,max(y.ans,x.ans));
    return temp;
}
namespace stree
{
    void update(int p)
    {
        maxx[p]=max(maxx[p<<1],maxx[p<<1|1]);
        minn[p]=min(minn[p<<1],minn[p<<1|1]);
        lans[p]=max(maxx[p<<1]-minn[p<<1|1],max(lans[p<<1],lans[p<<1|1]));
        rans[p]=max(maxx[p<<1|1]-minn[p<<1],max(rans[p<<1],rans[p<<1|1]));
    }
    void build(int p,int l,int r)
    {
        if(l<r)
        {
            int mid=(l+r)>>1;
            build(p<<1,l,mid),build(p<<1|1,mid+1,r);
            update(p);
        }
        else minn[p]=maxx[p]=val[pre[l]];
    }
    void putdown(int p)
    {
        maxx[p<<1]+=lazy[p],minn[p<<1]+=lazy[p],lazy[p<<1]+=lazy[p];
        maxx[p<<1|1]+=lazy[p],minn[p<<1|1]+=lazy[p],lazy[p<<1|1]+=lazy[p];
        lazy[p]=0;
    }
    void modify(int p,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y)
        {
            maxx[p]+=c,minn[p]+=c,lazy[p]+=c;
            return;
        }
        if(lazy[p]) putdown(p);
        int mid=(l+r)>>1;
        if(x<=mid&&y>=l) modify(p<<1,l,mid,x,y);
        if(y>mid&&x<=r) modify(p<<1|1,mid+1,r,x,y);
        update(p);
    }
    node getr(int p,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y) return node(maxx[p],minn[p],rans[p]);
        if(lazy[p]) putdown(p);
        int mid=(l+r)>>1;node tl=init,tr=init;
        if(x<=mid&&y>=l) tl=getr(p<<1,l,mid,x,y);
        if(y>mid&&x<=r) tr=getr(p<<1|1,mid+1,r,x,y);
        return merge(tl,tr);
    }
    node getl(int p,int l,int r,int x,int y)
    {
        if(x<=l&&r<=y) return node(maxx[p],minn[p],lans[p]);
        if(lazy[p]) putdown(p);
        int mid=(l+r)>>1;node tl=init,tr=init;
        if(x<=mid&&y>=l) tl=getl(p<<1,l,mid,x,y);
        if(y>mid&&x<=r) tr=getl(p<<1|1,mid+1,r,x,y);
        return merge(tr,tl);
    }
}
int solve(int x,int y)
{
    node L,R;L=R=init;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])
        {
            R=merge(stree::getr(1,1,n,dfn[top[y]],dfn[y]),R);
            stree::modify(1,1,n,dfn[top[y]],dfn[y]);
            y=fa[top[y]];
        }
        else
        {
            L=merge(L,stree::getl(1,1,n,dfn[top[x]],dfn[x]));
            stree::modify(1,1,n,dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
    }
    if(dep[x]<dep[y])
    {
        R=merge(stree::getr(1,1,n,dfn[x],dfn[y]),R);
        stree::modify(1,1,n,dfn[x],dfn[y]);
    }
    else
    {
        L=merge(L,stree::getl(1,1,n,dfn[y],dfn[x]));
        stree::modify(1,1,n,dfn[y],dfn[x]);
    }
    return merge(L,R).ans;
}
int main()
{
    n=input(),rt=rand()%n+1,init=node(0,inf,0);
    for(int i=1;i<=n;i++) val[i]=input();
    for(int i=1;i<n;i++)
    {
        a=input(),b=input();
        G[a].push_back(b),G[b].push_back(a);
    }
    get_siz(rt),get_way(rt,rt),stree::build(1,1,n);
    q=input();
    while(q--)
    {
        a=input(),b=input(),c=input();
        printf("%d\n",solve(a,b));
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值