题意:
给定一个长为 n 的数组 a,现在起点在 x,终点在 y,前进(向右)花费 R 的能量,后退花费 L 的能量,并且当在 i 位置时,可以花费 C 的能量瞬移到 j ( )。问到达终点需要花费的最少能量。(n <= 1e5, L, R, C, <= 1e9)
链接:
https://vjudge.net/problem/Gym-102279L
解题思路:
考虑最短路,对于相邻点,直接连有向边,重点在于瞬移如何建边。显然,不能直接两两连边,考虑到花费均为 C,对于同种权值,引一个辅助点 t,t 向该种权值点连权 的双向边,这样就达到两两连接的效果。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define sz(a) ((int)a.size())
#define mem(a, b) memset(a, b, sizeof a)
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 4e5 + 5;
const int maxm = 1e6 + 5;
const int mod = 1e9 + 7;
//const int inf = 0x3f3f3f3f;
typedef pair<ll, int> pli;
const ll inf = 0x3f3f3f3f3f3f3f3f;
vector<pli> G[maxn];
pli a[maxn];
ll dis[maxn];
int n, s, t;
priority_queue<pli, vector<pli>, greater<pli> > q;
void add(int u, int v, ll w){
G[u].pb({w, v});
}
ll dij(){
for(int i = 1; i <= n; ++i) dis[i] = inf;
dis[s] = 0, q.push({dis[s], s});
while(!q.empty()){
int u = q.top().second; ll d = q.top().first; q.pop();
if(d != dis[u]) continue;
for(int i = 0; i < sz(G[u]); ++i){
int v = G[u][i].second; ll w = G[u][i].first;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
return dis[t];
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
ll l, r, c; cin >> n >> l >> r >> c >> s >> t;
for(int i = 1; i <= n; ++i){
cin >> a[i].first; a[i].second = i;
}
sort(a + 1, a + 1 + n);
for(int i = 2; i <= n; ++i) add(i, i - 1, l * 2);
for(int i = 1; i < n; ++i) add(i, i + 1, r * 2);
int lim = n;
for(int i = 1, j; i <= lim; i = j){
++n; add(a[i].second, n, c), add(n, a[i].second, c);
for(j = i + 1; j <= lim && a[j].first == a[j - 1].first; ++j) add(a[j].second, n, c), add(n, a[j].second, c);
}
ll ret = dij() / 2;
cout << ret << endl;
return 0;
}