HDU 5052 Yaoge’s maximum profit(树剖+区间合并)

Yaoge’s maximum profit

 

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

 

 

Problem Description

Yaoge likes to eat chicken chops late at night. Yaoge has eaten too many chicken chops, so that Yaoge knows the pattern in the world of chicken chops. There are N cities in the world numbered from 1 to N . There are some roads between some cities, and there is one and only one simple path between each pair of cities, i.e. the cities are connected like a tree. When Yaoge moves along a path, Yaoge can choose one city to buy ONE chicken chop and sell it in a city after the city Yaoge buy it. So Yaoge can get profit if Yaoge sell the chicken chop with higher price. Yaoge is famous in the world. AFTER Yaoge has completed one travel, the price of the chicken chop in each city on that travel path will be increased by V .

 

 

Input

The first line contains an integer T (0 < T ≤ 10), the number of test cases you need to solve. For each test case, the first line contains an integer N (0 < N ≤ 50000), the number of cities. For each of the next N lines, the i-th line contains an integer Wi(0 < Wi ≤ 10000), the price of the chicken chop in city i. Each of the next N - 1 lines contains two integers X Y (1 ≤ X, Y ≤ N ), describing a road between city X and city Y . The next line contains an integer Q(0 ≤ Q ≤ 50000), the number of queries. Each of the next Q lines contains three integer X Y V(1 ≤ X, Y ≤ N ; 0 < V ≤ 10000), meaning that Yaoge moves along the path from city X to city Y , and the price of the chicken chop in each city on the path will be increased by V AFTER Yaoge has completed this travel.

 

 

Output

For each query, output the maximum profit Yaoge can get. If no positive profit can be earned, output 0 instead.

 

 

Sample Input

 

15123451 22 33 44 551 5 15 1 11 1 25 1 11 2 1

 

 

Sample Output

 

40010

 

题意:yaoge想要通过买卖鸡排赚差价,有n个城市,每个城市有一个鸡排价格,然后有n-1条边(将城市像树一样连起来),下面有q次操作,每次从城市u到城市v(走最短路),在这条路上的城市鸡排价格都增加x,求每次yaoge的最大收益

分析:

题目中已经说出了,给的是一棵树,操作数很多,而且每次对(u,v)的最短路进行操作,可以用(树剖)解决

现在我们开始考虑线段树应该维护哪些值?

由于每次从城市u到城市v,要完成买和卖的这个操作肯定是先买才能卖

所以我们需要维护的是一个先买再卖所得收益的最大值,但结合树剖来看,树剖中每个点都向他的祖先节点找,(x-->fx),(y-->fy),

比如这张图

现在要更新x-->y的路径,那么在树剖中,会更新 (fx-->x),(y-->fy)这样的链,由于我们想要的顺序是(x-->fx)(y-->fy)这种,所以在维护(fx-->x)时就要维护一个相反值,即先卖后买的最大值,这样 fx走到x先卖后买,那么反向考虑的话,x走到fx先买后卖(其实也是用了向量的思想)

于是维护两个值就行了,对于 (fx-->x)这样的链维护先卖后买的最大收益(rtl)

(y-->fy)这样正常方向的链,维护先买后卖的最大收益(ltr),

为了维护这两个值,还需要维护区间最大值(maxx)和区间最小值(minn)

这样 ltr=右儿子的maxx-左儿子的minn    rtl=左儿子的maxx-右儿子的minn

查询的时候还需要区间合并(这点详细看代码把)

树剖时,1.查询x-->fx这种的链,则查询从左到右的最大收益  2.查询y-->fy这种的链,则查询从右到左的最大收益

然后维护一个当前链的左边最小值(不在链内)minu,当前链右边最大值(不在链内)maxv,然后查询出(在链内)当前这条链的最小值tmin和最大值tmax

所以刷新最大收益为max( maxv-tmin , tmax-minu , 这条链的链内最大收益)

以上

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
const int N=5e4+10;
typedef long long ll;
ll siz[N],faz[N],tid[N],rnk[N],son[N],top[N],dep[N];
ll cnt,tot,a[N],first[N];
struct node
{
    ll l,r,lazy,maxx,minn,ltr,rtl;
    ll mid()
    {
        return (l+r)>>1;
    }
} t[N<<2];
struct edge
{
    ll v,next;
}g[N<<2];
void add(ll u,ll v)
{
    g[tot].v=v;
    g[tot].next=first[u];
    first[u]=tot++;
}
void dfs1(ll u,ll father,ll depth)
{
    siz[u]=1;
    faz[u]=father;
    dep[u]=depth;
    for(ll i=first[u]; ~i; i=g[i].next)
    {
        ll v=g[i].v;
        if(v!=father)
        {
            dfs1(v,u,depth+1);
            siz[u]+=siz[v];
            if(son[u]==-1||siz[v]>siz[son[u]])
                son[u]=v;
        }
    }
}
void dfs2(ll u,ll t)
{
    tid[u]=++cnt;
    rnk[cnt]=u;
    top[u]=t;
    if(son[u]==-1)return;
    dfs2(son[u],t);
    for(ll i=first[u]; ~i; i=g[i].next)
    {
        ll v=g[i].v;
        if(v!=faz[u]&&v!=son[u])
            dfs2(v,v);
    }
}
void pushup(ll rt)
{
    t[rt].maxx=max(t[lson].maxx,t[rson].maxx);
    t[rt].minn=min(t[lson].minn,t[rson].minn);
    t[rt].ltr=max(t[rson].maxx-t[lson].minn,max(t[lson].ltr,t[rson].ltr));
    t[rt].rtl=max(t[lson].maxx-t[rson].minn,max(t[lson].rtl,t[rson].rtl));
}
void pushdown(ll rt)
{
    if(t[rt].lazy)
    {
        t[lson].lazy+=t[rt].lazy;
        t[rson].lazy+=t[rt].lazy;
        t[lson].maxx+=t[rt].lazy;
        t[rson].maxx+=t[rt].lazy;
        t[lson].minn+=t[rt].lazy;
        t[rson].minn+=t[rt].lazy;
        t[rt].lazy=0;
    }
}
void build(ll l,ll r,ll rt)
{
    t[rt].l=l,t[rt].r=r,t[rt].lazy=0;
    if(l==r)
    {
        t[rt].maxx=t[rt].minn=a[rnk[l]];
        t[rt].ltr=t[rt].rtl=0;
        return;
    }
    ll m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}

void update(ll ql,ll qr,ll rt,ll w)
{
    if(ql<=t[rt].l&&t[rt].r<=qr)
    {
        t[rt].lazy+=w;
        t[rt].maxx+=w;
        t[rt].minn+=w;
        return;
    }
    pushdown(rt);
    ll m=t[rt].mid();
    if(ql<=m)update(ql,qr,lson,w);
    if(qr>m)update(qr,qr,rson,w);
    pushup(rt);
}
ll query(ll ql,ll qr,ll rt,ll flag,ll &tmax,ll &tmin)
{
    if(ql<=t[rt].l&&t[rt].r<=qr)
    {
        tmax=t[rt].maxx;
        tmin=t[rt].minn;
        if(flag)return t[rt].ltr;//从左到右
        else return t[rt].rtl;//从右到左
    }
    pushdown(rt);
    ll m=t[rt].mid();
    if(qr<=m)return query(ql,qr,lson,flag,tmax,tmin);
    else if(ql>m)return query(ql,qr,rson,flag,tmax,tmin);
    else//区间合并
    {
        ll ans=0,maxl,minl,maxr,minr;
        ans=max(query(ql,m,lson,flag,maxl,minl),query(m+1,qr,rson,flag,maxr,minr));
        if(flag)ans=max(ans,maxr-minl);
        else ans=max(ans,maxl-minr);
        tmax=max(maxl,maxr);
        tmin=min(minl,minr);
        return ans;
    }
}
ll solve(ll u,ll v,ll w)
{
    ll maxv=0,minu=t[1].maxx,ans=0,tmax,tmin;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]>=dep[top[v]])
        {
            ans=max(ans,query(tid[top[u]],tid[u],1,0,tmax,tmin));//u则查询从左到右
            ans=max(ans,maxv-tmin);
            ans=max(ans,tmax-minu);
            minu=min(minu,tmin);
            update(tid[top[u]],tid[u],1,w);
            u=faz[top[u]];
        }
        else
        {
            ans=max(ans,query(tid[top[v]],tid[v],1,1,tmax,tmin));//v则从右到左
            ans=max(ans,maxv-tmin);
            ans=max(ans,tmax-minu);
            maxv=max(maxv,tmax);
            update(tid[top[v]],tid[v],1,w);
            v=faz[top[v]];
        }
    }
    if(dep[u]>=dep[v])
    {
        ans=max(ans,query(tid[v],tid[u],1,0,tmax,tmin));
        ans=max(ans,maxv-tmin);
        ans=max(ans,tmax-minu);
        update(tid[v],tid[u],1,w);
    }
    else
    {
        ans=max(ans,query(tid[u],tid[v],1,1,tmax,tmin));
        ans=max(ans,tmax-minu);
        ans=max(ans,maxv-tmin);
        update(tid[u],tid[v],1,w);
    }
    return ans;
}
void init(ll n)
{
    memset(son,-1,sizeof(son));
    memset(first,-1,sizeof(first));
    cnt=tot=0;
}
int main()
{
    ll t,u,v,w,n,q;
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld",&n);
        init(n);
        for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
        for(ll i=2;i<=n;i++)
        {
            scanf("%lld%lld",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs1(1,1,1);
        dfs2(1,1);
        build(1,n,1);
        scanf("%lld",&q);
        while(q--)
        {
            scanf("%lld%lld%lld",&u,&v,&w);
            printf("%lld\n",solve(u,v,w));
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值