题意:
给定
n
n
n 点高度为
a
i
a_i
ai,初始位置为
1
1
1,求从
1
1
1 出发的最长路(经过边权的代数和最大)是多少
两个点之间的边权这样定义:
现在从
x
x
x 要去
y
y
y
如果
a
x
>
a
y
a_x>a_y
ax>ay,那么边权为
a
x
−
a
y
a_x-a_y
ax−ay
如果
a
x
<
a
y
a_x<a_y
ax<ay,那么边权为
−
2
∗
(
a
y
−
a
x
)
-2*(a_y-a_x)
−2∗(ay−ax)
相等边权为
0
0
0
思路:
设终点的高度为
k
<
a
1
k < a_1
k<a1
那么途中经过的正边权和为
k
−
a
1
k-a_1
k−a1,最后的答案至多为这个
由于可能经过负边权,这会使得最后的答案更小
如果终点固定,那么我们需要最小化其中经过的负边权,这样才能最后剩余最多
然后考虑建这样一个图
下坡路边权设为
0
0
0,上坡路设为
a
[
t
o
]
−
a
[
n
o
w
]
a[to]-a[now]
a[to]−a[now]
求出起点为
1
1
1 的单源最短路,即最小化上坡负边权的和
枚举终点维护最大值
至于为什么建的边是一倍,而不是两倍,是因为上升之后下过原本的高度就会抵消掉
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int a[maxn];
vector <int> e[maxn];
ll dis[maxn];
bool vis[maxn];
void dij(){
priority_queue <pair<int,int>, vector<pair<int,int> >, greater<pair<int,int> > > q;
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
q.push({0, 1});
while(!q.empty()){
int now = q.top().second;q.pop();
if(vis[now]) continue;
vis[now] = 1;
for(auto to : e[now]){
ll d = max(0, a[to] - a[now]);
if(dis[to] > dis[now] + d){
dis[to] = dis[now] + d;
q.push({dis[to], to});
}
}
}
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int i = 1; i <= m; ++i){
int x, y;cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dij();
ll ans = 0;
for(int i = 1; i <= n; ++i)
ans = max(ans, a[1] - a[i] - dis[i]);
cout << ans;
}
int main()
{
ios::sync_with_stdio(0);
// int TT;cin>>TT;while(TT--)
work();
return 0;
}