wyh的商机(tarjan离线求lca)

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
一天,你们wyh学长和你们zhl学长玩一个游戏,这个游戏规则是这样的

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

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

游戏一共进行Q轮。

每轮给你2个点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
示例1
输入
复制
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

分析:

将城市看作一棵树,每两个城市之间有一个最近的公共祖先,那么获取的最大利益有三种可能
1.s-lca已经完成了买卖
2.lca-t已经完成了买卖
3.s-lca完成了买,lca-t完成了卖
所以up数字记录叶子节点到其祖先的最大买卖值,down数组记录祖先到叶子节点的最大买卖值,mx数组记录叶子节点到祖先节点的最大价格,mn数组记录叶子节点到祖先节点的最小价格。
因为要查询很多的情况,所以利用tarjan离线求得lca

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 7;
int n, m, mn[N], mx[N], up[N], down[N], ans[N], fa[N];
int vis[N];
struct node {
    int s, t, id;
};
vector<node> g[N],q[N],e[N];
int Find(int x) {
    if(fa[x] == x)
        return x;
    int root = Find(fa[x]);
    up[x] = max(max(up[x], up[fa[x]]), mx[fa[x]] - mn[x]);
    down[x] = max(max(down[x], down[fa[x]]), mx[x] - mn[fa[x]]);
    mn[x] = min(mn[x], mn[fa[x]]);
    mx[x] = max(mx[x], mx[fa[x]]);
    return fa[x] = root;
}

void dfs(int u) {
    vis[u] = 1;
    

    for(int i=0;i<g[u].size();i++) {//遍历树
    	node v=g[u][i];
        if(!vis[v.s]) {
            dfs(v.s);
            fa[v.s] = u;
        }
    }
	for(int i=0;i<q[u].size();i++) {//找查询
    	node v=q[u][i];
        if(vis[v.s]) {
            int lca = Find(v.s);
            if(v.id > 0) {
                e[lca].push_back({u, v.s, v.id});
            } else {
                e[lca].push_back({v.s, u, -v.id});
            }
        }
    }
    for(int i=0;i<e[u].size();i++) {//计算获得的最大利益
    	node v=e[u][i];
        int x = v.s, y = v.t, id = v.id;
        Find(x); Find(y);
        ans[id] = max(mx[y] - mn[x], max(up[x], down[y]));
    }
}
int main() {

    scanf("%d", &n);

    for(int i = 1; i <= n; i++) {
        int w; scanf("%d", &w);
        mn[i] = mx[i] = w;
        fa[i] = i;
    }
	int a,b;
	node v;
    for(int i = 1; i < n; i++) {
         scanf("%d%d", &a, &b);
        v.s=b;
       g[a].push_back(v);
       v.s=a;
       g[b].push_back(v);
    }

    scanf("%d", &m);
    
    for(int i = 1; i <= m; i++) {
        int s, t; scanf("%d%d", &s, &t);
        v.s=t,v.id=i;
        q[s].push_back(v);
        v.s=s,v.id=-i;
        q[t].push_back(v);
    }

    dfs(1);

    for(int i = 1; i <= m; i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值