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进行卖。
那么我们维护三个东西:
我们在树上维护呢?
和上面一样,就是要么是在x-y进行买卖最大值,y-z买卖的最大值。或者在x-y买,在y-z进行卖。
down和up的区别是在z-y进行买, 在y-x卖。
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于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)