HDU 5052 Yaoge’s maximum profit(树链剖分——点权,区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5052

题意:

给出一棵树,每个点有商店,每个商店都有一个价格,Yaoge每次从x走到y都可以在一个倒卖商品,从中得取利益,当然,买一顶要在卖之前。但是没次走过一条路,这条路上的所有商品都会增加一个v。

输出每次的最大利益。

分析:

这道题,首先考虑一条链的版本,我们用线段树来维护,先从区间合并来考虑。对于当前结点,那么结点的最优值肯定来自于左孩子和右孩子的最优值,以及区间合并的最优值。区间合并的时候,我们肯定是在左孩子的区间买,在右孩子的区间卖,最大差值肯定是右孩子的最大值-左孩子的最小值。区间合并搞定了。更新操作也很容易,加个懒惰标记,由于区间内全部加上V值,对于区间内的差价是没有影响的,所以直接累加到标记上就可以。那么来到这题,变成多条链的合并。我们会发现,如果是从X到Y的方向,求解方法同上。但是如果我们是Y到X的方向,我们应该反过来,即最大差值=左孩子的最大值-右孩子的最小值。

AC代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1

const int maxn = 5*1e4 + 100;
int sz[maxn], deep[maxn], fa[maxn], son[maxn];
int p[maxn], fp[maxn], top[maxn], pos;

struct Node
{
    int Max, Min, inc, des;
}A[maxn<<2];
int Add[maxn<<2];

int weight[maxn];
vector<int> G[maxn];
int n;

void dfs1(int u, int f, int d)
{
    deep[u] = d;
    fa[u] = f;
    sz[u] = 1;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(v == f)
            continue;
        dfs1(v, u, d+1);
        sz[u] += sz[v];
        if(son[u]==-1 || sz[v]>sz[son[u]])
        {
            son[u] = v;
        }
    }
}
void dfs2(int u, int sp)
{
    top[u] = sp;
    p[u] = ++pos;
    fp[pos] = u;
    if(son[u] != -1)
        dfs2(son[u], sp);
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(v==fa[u] || v==son[u])
            continue;
        dfs2(v, v);
    }
}

void PushUp(int rt)
{
    A[rt].Max = max(A[rt<<1].Max, A[rt<<1|1].Max);
    A[rt].Min = min(A[rt<<1].Min, A[rt<<1|1].Min);
    A[rt].inc = max(A[rt<<1|1].Max-A[rt<<1].Min, max(A[rt<<1].inc, A[rt<<1|1].inc));
    A[rt].des = max(A[rt<<1].Max-A[rt<<1|1].Min, max(A[rt<<1].des, A[rt<<1|1].des));
}
void PushDown(int rt)
{
    if(Add[rt] != 0)
    {
        Add[rt<<1] += Add[rt];
        Add[rt<<1|1] += Add[rt];
        A[rt<<1].Max += Add[rt];
        A[rt<<1].Min += Add[rt];
        A[rt<<1|1].Max += Add[rt];
        A[rt<<1|1].Min += Add[rt];
        Add[rt] = 0;
    }
}
void build(int l, int r, int rt)
{
    Add[rt] = 0;
    if(l == r)
    {
        A[rt].Max = A[rt].Min = weight[fp[l]];
        A[rt].inc = A[rt].des = 0;
        return ;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int L, int R, int c, int l, int r, int rt)
{
    if(L<=l && r<=R)
    {
        Add[rt] += c;
        A[rt].Max += c;
        A[rt].Min += c;
        return ;
    }
    PushDown(rt);
    int m = (l+r)>>1;
    if(L <= m)
        update(L, R, c, lson);
    if(R > m)
        update(L, R, c, rson);
    PushUp(rt);
}
int Merge(int& Max, int& Min, int& Max1, int& Min1, int f)
{
    int res = f? (Max-Min1) : (Max1-Min);
    Max = max(Max, Max1);
    Min = min(Min, Min1);
    return res;
}
int query(int& Max, int& Min, int L, int R, int f, int l, int r, int rt)
{
    if(L<=l && r<=R)
    {
        Max = A[rt].Max;
        Min = A[rt].Min;
        return f? A[rt].des : A[rt].inc;
    }
    PushDown(rt);
    int m = (l+r)>>1;
    int Max1, Min1, res = 0;
    int ok1 = 0, ok2 = 0;
    if(L <= m)
    {
        res = max(res, query(Max, Min, L, R, f, lson));
        ok1 = 1;
    }
    if(R > m)
    {
        res = max(res, query(Max1, Min1, L, R, f, rson));
        ok2 = 1;
    }
    if(ok1==1 && ok2==1)
    {
        res = max(res, Merge(Max, Min, Max1, Min1, f));
    }
    else if(ok1==0 && ok2==1)
    {
        Max = Max1;
        Min = Min1;
    }
    PushUp(rt);
    return res;
}

void init()
{
    for(int i = 0; i <= n; i++)
        G[i].clear();
    memset(son, -1, sizeof(son));
    memset(weight, 0, sizeof(weight));
    pos = 0;
}

void Change_update(int u, int v, int c)
{
    int f1 = top[u], f2 = top[v];
    while(f1 != f2)
    {
        if(deep[f1] < deep[f2])
        {
            swap(u, v);
            swap(f1, f2);
        }
        update(p[f1], p[u], c, 1, n, 1);
        u = fa[f1];
        f1 = top[u];
    }
    if(deep[u] > deep[v])
    {
        swap(u, v);
    }
    update(p[u], p[v], c, 1, n, 1);
}
int Change_query(int u, int v)
{
    int f1 = top[u], f2 = top[v];
    int Max1, Min1, Max2, Min2;
    int Max, Min;
    int ans = 0, tmp = 0;
    int ok1 = 0, ok2 = 0;
    while(f1 != f2)
    {
        if(deep[f1] > deep[f2])
        {
            tmp = query(Max, Min, p[f1], p[u], 1, 1, n, 1);
            ans = max(ans, tmp);
            if(ok1)
            {
                tmp = Merge(Max1, Min1, Max, Min, 0);
                ans = max(ans, tmp);
            }
            else
            {
                Max1 = Max;
                Min1 = Min;
                ok1 = 1;
            }
            u = fa[f1];
            f1 = top[u];
        }
        else
        {
            tmp = query(Max, Min, p[f2], p[v], 0, 1, n, 1);
            ans = max(ans, tmp);
            if(ok2)
            {
                tmp = Merge(Max2, Min2, Max, Min, 1);
                ans = max(ans, tmp);
            }
            else
            {
                Max2 = Max;
                Min2 = Min;
                ok2 = 1;
            }
            v = fa[f2];
            f2 = top[v];
        }
    }
    if(deep[u] > deep[v])
    {
        tmp = query(Max, Min, p[v], p[u], 1, 1, n, 1);
    }
    else
    {
        tmp = query(Max, Min, p[u], p[v], 0, 1, n, 1);
    }
    ans = max(ans, tmp);
    if(ok1)
    {
        tmp = Merge(Max1, Min1, Max, Min, 0);
        ans = max(ans, tmp);
    }
    else
    {
        Max1 = Max;
        Min1 = Min;
    }
    if(ok2)
    {
        tmp = Merge(Max1, Min1, Max2, Min2, 0);
        ans = max(ans, tmp);
    }
    return ans;
}

int main()
{
    int T, q, a, b, c;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        init();
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &weight[i]);
        }
        for(int i = 1; i < n; i++)
        {
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        dfs1(1, -1, 1);
        dfs2(1, 1);
        build(1, n, 1);
        scanf("%d", &q);
        while(q--)
        {
            scanf("%d%d%d", &a, &b, &c);
            int ans = Change_query(a, b);
            printf("%d\n", ans);
            Change_update(a, b, c);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值