wyh的商机 树上倍增,树链上买卖问题

题目

wyh的商机(https://ac.nowcoder.com/acm/problem/15427)

题目描述

一天,你们wyh学长和你们zhl学长玩一个游戏,这个游戏规则是这样的

给你n个城市,保证这n个城市之间都只有一条道路可以到达。

有一件物品,在所有城市中都是一样的,但是由于各个城市的经济发展不同,导致每个城市对于这件物品销售的价格不同。

游戏一共进行Q轮。

每轮给你2个点s和t,其中s代表起点,t代表终点。

对于每一个城市都可以选择买这个物品,如果手里有这个物品的话,也可以选择卖掉这个物品,对于同一个城市来说,买和卖的价格是一样的,每一个城市只能买一件物品

从s-t只能进行一次买卖,现在,你们wyh学长和zhl学长都需要找到一个方案,使得从起点走到终点的时候盈利最多,请你帮助帮助他们吧

输入描述:

每个测试文件中都只有一组测试数据

输入第一行一个整数n(1<=n<=50000),代表有n个城市

第二行输入n个数,代表每个城市对于这件物品的价格wi(1<=wi<=50000)

接下来有n-1行,每行2个数a和b,代表a到b之间有一条路

接下来输入一个数Q(1<=Q<=50000)

接下来Q行,每行2个数s和t

输出描述:

对于每组测试数据,输出对应答案,如果从起点到终点不能盈利的话,输出0

输入

4
1
5
3
2
1 3
3 2
3 4
9
1 2
1 3
1 4
2 3
2 1
2 4
3 1
3 2
3 4

输出

4
2
2
0
0
0
0
2
0
对于一个询问u-v。我们把询问分成u-LCA和LCA-v。
那么最优值可以是:在u-LCA进行买卖,在LCA-v进行买卖.
在u-LCA进行买在LCA-v进行卖。
那么我们维护三个东西:
up[i][j]i2j
down[i][j]x2ji
mx[i][j]:i2j
mi[i][j]:i2j
我们在树上维护呢?

和上面一样,就是要么是在x-y进行买卖最大值,y-z买卖的最大值。或者在x-y买,在y-z进行卖。
up[x][j+1]=max(max(up[x][j],up[y][j]),mx[y][j]mi[x][j])
down和up的区别是在z-y进行买, 在y-x卖。
up[x][j+1]=max(max(up[x][j],up[y][j]),mx[x][j]mi[y][j])
mi和mx的维护就很简单了。
我们处理查询。对于一条链,我们每次只合并两个区间,同样的方法处理。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
struct Edge {
    int to, nxt;
} e[100000+10];
int head[50010], cut=0;
void Addedge(int x, int y) {
    e[++cut]= {y, head[x]};
    head[x]=cut;
}
LL w[50010];
int f[50010][22], deep[50010];
LL mx[50010][22], mi[50010][22], up[50010][22], down[50010][22];
int n;

void DFS(int u, int fa, int d) {
    f[u][0]=fa;
    deep[u]=d;
    up[u][0]=max(w[fa]-w[u], 0ll);
    down[u][0]=max(w[u]-w[fa], 0ll);
    mx[u][0]=max(w[fa], w[u]);
    mi[u][0]=min(w[fa], w[u]);
    for(int i=head[u]; i; i=e[i].nxt) {
        int to=e[i].to;
        if(to!=fa) {
            DFS(to, u, d+1);
        }
    }
}

void init() {
    memset(mx, 0, sizeof(mx));
    memset(mi, 0x3f, sizeof(mi));
    DFS(1, 0, 0);
    for(int k=0; k<20; k++) {
        for(int i=1; i<=n; i++) {
            if(f[i][k]==0) {
                f[i][k+1]=0;
            } else {
                f[i][k+1]=f[f[i][k]][k];
                int t=f[i][k];
                mx[i][k+1]=max(mx[i][k], mx[t][k]);
                mi[i][k+1]=min(mi[i][k], mi[t][k]);
                up[i][k+1]=max(max(up[i][k], up[t][k]), mx[t][k]-mi[i][k]);
                down[i][k+1]=max(max(down[i][k], down[t][k]), mx[i][k]-mi[t][k]);
            }
        }
    }
}

int LCA(int x, int y) {
    if(deep[x]<deep[y])
        swap(x, y);
    for(int i=20; deep[x]!=deep[y]; i--) {
        if(deep[f[x][i]]>=deep[y]) {
            x=f[x][i];
        }
    }
    if(x==y)
        return x;
    for(int i=20; i>=0; i--) {
        if(f[x][i]!=f[y][i]) {
            x=f[x][i], y=f[y][i];
        }
    }
    return f[x][0];
}

LL getup(int x, int k, LL &mi_uf) {
    mi_uf=1ll<<60;//记录已经走过的树链的最小值,最后是u-lca的最小值
    LL res=0;

    for(int i=20; i>=0; i--) {
        if(k&(1<<i)) {
            res=max(res, up[x][i]);
            res=max(res, mx[x][i]-mi_uf);
            mi_uf=min(mi_uf, mi[x][i]);
            x=f[x][i];
        }
    }
    return res;
}

LL getdown(int x, int k, LL &mx_fv) {
    mx_fv=0;//记录已经走过的树链的最大值,最后是lca-v的最小值
    LL res=0;
    for(int i=20; i>=0; i--) {
        if(k&(1<<i)) {
            res=max(res, down[x][i]);
            res=max(res, mx_fv-mi[x][i]);
            mx_fv=max(mx_fv, mx[x][i]);
            x=f[x][i];
        }
    }
    return res;
}

int main() {
    while(~scanf("%d", &n)) {
        cut=0; memset(head, 0, sizeof(head));
        for(int i=1; i<=n; i++) {
            scanf("%lld", &w[i]);
        }
        for(int i=1; i<n; i++) {
            int x, y;
            scanf("%d%d", &x, &y);
            Addedge(x, y);
            Addedge(y, x);
        }
        init();
        int q;
        scanf("%d", &q);
        while(q--) {
            int x, y;
            scanf("%d%d", &x, &y);
            int lca=LCA(x, y);
            LL mi_uf, mx_fv;
            LL s_uf=getup(x, deep[x]-deep[lca], mi_uf);
            LL s_fv=getdown(y, deep[y]-deep[lca], mx_fv);
            LL ans=max(max(s_uf, s_fv), mx_fv-mi_uf);
            printf("%lld\n", ans);
        }
    }

    return 0;
}

posted @   liweihang  阅读(216)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『wyh的商机 树上倍增,树链上买卖问题』
点击右上角即可分享
微信分享提示