第二回全国統一プログラミング王決定戦予選 D - Shortest Path on a Line(600)
概要
1からNまでの番号が付いた頂点がある。最初は辺がない。
M個のパラメータLi, Ri, Ciが与えられる。パラメータLi, Ri, Ciに対し、下記を行う。
- Li ≦ s < t ≦ Ri なる全ての頂点(s, t)に対し、長さCiの辺を追加する
上記の作業をすべて終えた後、頂点1から頂点Nまでの最短距離を求めよ。
(頂点1から頂点Nまでたどり着けない場合は-1を出力せよ)
制約
2 ≦ N ≦ 10^5
1 ≦ M ≦ 10^5
1 ≦ Ci ≦ 10^9
方針①
d[i] :頂点1から頂点iまでの最短距離 とすると、dは広義単調増加である。
そのため、1 ≦ i ≦ N - 1なるiについて、(i+1, i)を結ぶ長さ0の辺を追加しても、最短距離に影響しない。
また、M個の各パラメータ(Li, Ri, Ci)について、このパラメータを(Li, Ri)を結ぶ長さCiの辺として考える。
このとき、上記をあわせたN - 1 + M本の辺でもって頂点1から頂点Nまでの最短距離が実現される。
最短距離はDijkstra法で計算すればよい。
方針②
パラメータ(Li, Ri, Ci)によって追加される辺について、終点がRiのものだけを考えればよい。
このとき、dp[i]:頂点1から頂点iまでの最短距離 とすると、dpは下記のように遷移する。
dp[x] = min_{i | x = Ri}(min_{Li ≦ s < Ri}(dp[s] + Ci ))
Riの小さい順にdpが確定してゆくことになるので、Riが小さい順に上記dpを遷移させてゆく。
dpの区間最小を取得する必要があるため、dpはSegmentTreeで実装する。
解答①
Submission #9322163 - NIKKEI Programming Contest 2019-2
#include <bits/stdc++.h> #define rep(i,n) for(int i=(0);i<(n);i++) using namespace std; typedef long long ll; // dijkstra 蟻本 struct edge {ll to, cost;}; typedef pair<ll, ll> P; // firstはsからの最短距離, secondは頂点の番号 const ll INF = LLONG_MAX / 2; const int max_n = 101010; int n; vector<edge> g[max_n]; ll d[max_n]; void dijkstra(ll s){ fill(d, d + n, INF); d[s] = 0; priority_queue<P,vector<P>,greater<P> > que; que.push(P(0,s)); while(!que.empty()){ P p = que.top(); que.pop(); ll v = p.second; if(d[v] < p.first) continue; //後から最短パスが見つかった場合、先にqueに入っているものはここではじかれる for(ll i = 0; i < g[v].size(); i++){ edge e= g[v][i]; if(d[e.to] > d[v] + e.cost){ d[e.to] = d[v] + e.cost; que.push(P(d[e.to], e.to)); } } } } int main() { cin.tie(0); ios::sync_with_stdio(false); int m; cin >> n >> m; rep(i, m){ int l, r; ll c; cin >> l >> r >> c; l--; r--; g[l].push_back((edge){r, c}); } rep(i, n){ g[i+1].push_back((edge){i, 0}); } dijkstra(0); ll dist = d[n-1]; if(dist == INF){ cout << -1 << endl; }else{ cout << dist << endl; } }
解答②
Submission #9322063 - NIKKEI Programming Contest 2019-2
#include <bits/stdc++.h> #define rep(i,n) for(int i=(0);i<(n);i++) using namespace std; typedef long long ll; struct edge{ int l, r; ll c; }; template< typename Monoid > struct SegmentTree { using F = function< Monoid(Monoid, Monoid) >; int sz; vector< Monoid > seg; const F f; const Monoid M1; SegmentTree(int n, const F f, const Monoid &M1) : f(f), M1(M1) { sz = 1; while(sz < n) sz <<= 1; seg.assign(2 * sz, M1); } void set(int k, const Monoid &x) { seg[k + sz] = x; } void build() { for(int k = sz - 1; k > 0; k--) { seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]); } } void update(int k, const Monoid &x) { k += sz; seg[k] = x; while(k >>= 1) { seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]); } } Monoid query(int a, int b) { Monoid L = M1, R = M1; for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) { if(a & 1) L = f(L, seg[a++]); if(b & 1) R = f(seg[--b], R); } return f(L, R); } Monoid operator[](const int &k) const { return seg[k + sz]; } }; int main() { cin.tie(0); ios::sync_with_stdio(false); int n, m; cin >> n >> m; vector<edge> edges; rep(i, m){ int l, r; ll c; cin >> l >> r >> c; l--; r--; edges.push_back((edge){l, r, c}); } sort(edges.begin(), edges.end(), [](edge e1, edge e2){ return e1.r < e2.r; }); ll INF = LLONG_MAX / 2; SegmentTree<ll> seg(n, [](ll a, ll b) { return min(a, b); }, INF); seg.update(0, 0); for(edge e : edges){ ll mn = seg.query(e.l, e.r); ll val = seg.query(e.r, e.r+1); seg.update(e.r, min({val, mn + e.c, INF})); } ll ans = seg.query(n - 1, n); if(ans == INF){ cout << -1 << endl; }else{ cout << ans << endl; } }