Description
給定 $n$ 點,這 $n$ 個點由 $m$ 班列車穿插連結。對於第 $i$ 班列車,會在 $p_i$ 時刻從 $x_i$ 站點出發開向 $y_i$ 站點,到站時間爲 $q_i$。如今從 $1$ 號節點出發,通過屢次換乘到達節點 $n$。一次換乘是指對於兩班列車,假設分別爲 $u$ 號與 $v$ 號列車,若 $y_u = x_v$ 而且 $q_u \leq p_v$,那麼小貓能夠乘坐完 $u$ 號列車後在 $y_u$ 號站點等待 $p_v - q_u$ 個時刻,並在時刻 $p_v$ 乘坐 $v$ 號列車。要求最小化路程中的煩躁值。煩躁值的計算方法是:給定參數 $A,B,C$,對於每次等待,假設等待了 $t$ 個時刻,那麼煩躁值增長 $A t^2 + B t + C$。另外若是在時刻 $T$ 到達節點 $n$,則狂躁值再增長 $T$。算法
Limitations
$1 \leq n,~m \leq 2 \times 10^5$this
$0 \leq A \leq 10,~~0 \leq B,~C \leq 10^6$spa
$1 \leq p_i \leq q_i \leq 10^3$code
保證 $1$ 能夠到達 $n$。排序
Solution
好像正解是以時間爲狀態DP來着……然而拿到這個題的第一反應是DP每條邊到 $n$ 的貢獻。隊列
考慮設 $f_i$ 是第 $i$ 條邊開始出發,到達 $n$ 號節點對煩躁值的最小貢獻。因而有轉移方程:ip
$$f_i = \min_{p_j \geq q_i} ^{x_j = y_i} {f_j + A (p_j - q_i)^2 + B(p_j - q_i) + C + p_j - q_i} + q_i - p_i$$it
即io
$$f_i = \min_{p_j \geq q_i} ^{x_j = y_i} {f_j + A p_j^2 - 2Ap_jq_i + Aq_i^2 + Bp_j - Bq_i + C + p_j - q_i} + q_i - p_i$$class
將與 $j$ 無關的項提出大括號,獲得
$$f_i = \min_{p_j \geq q_i} ^{x_j = y_i} {f_j + A p_j^2 - 2Ap_jq_i + Bp_j + p_j } + q_i - p_i+ Aq_i^2- q_i- Bq_i + C $$
整理得
$$f_i = \min_{p_j \geq q_i} ^{x_j = y_i} {f_j + A p_j^2 - 2Ap_jq_i + (B+1)p_j} + Aq_i^2- Bq_i + C- p_i $$
設 $g_i = A q_i^2 - Bq_i - p_i + C$,$h_i = Ap_i^2 + (B+1)p_j$
上式即爲
$$f_i = f_j + h_j - 2Ap_jq_i + g_i$$
移相獲得
$$f_j + h_j = 2Aq_iq_j + f_i - g_i$$
注意到 $g_i$ 是一個與 $i$ 有關的常數,那麼最小化 $f_i$ 只須要最小化 $f_i + g_i$
若是將 $f_j + h_j$ 看做縱座標, $q_j$ 看做橫座標,上述方程能夠當作一條斜率爲常數 $2Aq_i$ 的直線,在全部知足條件的 $j$ 中選擇一個點,使得直線過這個點,最小化直線在 $y$ 軸上的截距 $f_i + g_i$。
那麼全部知足條件的點顯然在一個下突殼上,證實上能夠考慮若是一個點不在下突殼上那麼能找到一個更優的 $j$。
而後考慮若是 $i$ 會從 $j$ 轉移過來,那麼必定有 $q_i \leq p_j$,又由於 $p_j < q_j$,所以 $q_i < q_j$,因而按照 $q$ 的不升序進行排序便可。
在轉移時,只須要在每一個點維護一個凸殼表示全部可能被選擇的點,而後再維護一個 set
記錄該點上已經被計算可是沒有插入凸殼的點。set
內部按照 $p$ 的不升序排序,每次要轉移一條邊 $i$ 的時候先將終點中 $p$ 不小於 $q_i$ 的邊插入凸殼,因爲轉移是按照 $q_i$ 的順序進行的,已經被插入凸殼的點必定是合法能夠轉移的。
另外注意到 $q_i$ 是單調不升的,因而斜率 $2Aq_i$ 也是單調升的,再考慮到插入的橫軸 $p_j$ 也是單調不升的,所以可使用單調隊列維護每一個點的凸殼便可。
須要注意的一點細節是凸殼的橫軸是從大到小插入的,在維護的時候不要把大於號小於號寫反。
一共進行了 $m$ 次轉移,每次轉移複雜度 $O(1)$,所以 DP 過程的時間複雜度是 $O(m)$,可是因爲進行了排序,且每條邊插入在 set
中 1 次,因此整個算法的時間複雜度 $O(m \log m)$。
Code
#include <cstdio> #include <set> #include <queue> #include <algorithm> const int maxn = 200005; const ll INF = 1ll << 50; int n, m; ll A, B, C, ans; ll frog[maxn], g[maxn], h[maxn]; struct M { int x, y, p, q; inline bool operator<(const M &_others) const { return this->q > _others.q; } }; M MU[maxn]; struct Cmp { inline bool operator() (const int &_a, const int &_b) { if (MU[_a].p != MU[_b].p) { return MU[_a].p > MU[_b].p; } else { return _a < _b; } } }; std::deque<int>Q[maxn]; std::set<int, Cmp>s[maxn]; int query(const int x); void free(const int x, const int y); void insert(const int x, const int y); signed main() { freopen("route.in", "r", stdin); freopen("route.out", "w", stdout); qr(n); qr(m); qr(A); qr(B); qr(C); for (int i = 1; i <= m; ++i) { qr(MU[i].x); qr(MU[i].y); qr(MU[i].p); qr(MU[i].q); } frog[m + 1] = 1ll << 50; std::sort(MU + 1, MU + 1 + m); for (int i = 1; i <= m; ++i) { g[i] = A * MU[i].q * MU[i].q - B * MU[i].q - MU[i].p + C; h[i] = A * MU[i].p * MU[i].p + (B + 1) * MU[i].p; } for (int i = 1; i <= m; ++i) if (MU[i].y == n) { frog[i] = MU[i].q - MU[i].p; s[MU[i].x].insert(i); } else { int y = MU[i].y; free(y, MU[i].q); int j = query(i); if (!j) { frog[i] = INF; continue; } frog[i] = frog[j] + g[i] + h[j] - ((A * MU[i].q * MU[j].p) << 1); s[MU[i].x].insert(i); } ans = 1ll << 50; for (int i = 1; i <= m; ++i) if (MU[i].x == 1) { ans = std::min(frog[i] + A * MU[i].p * MU[i].p + B * MU[i].p + C + MU[i].p, ans); } qw(ans, '\n', true); return 0; } void free(const int x, const int y) { while (!s[x].empty()) { auto u = *s[x].begin(); if (MU[u].p >= y) { insert(x, u); s[x].erase(u); } else { break; } } } inline std::pair<ll, ll> calc(const int x) { return std::make_pair(frog[x] + h[x], 1ll * MU[x].p); } inline bool judge(const int x, const int y, const int z) { auto i = calc(x), j = calc(y), k = calc(z); return (k.first <= j.first) && ((i.first - k.first) * (i.second - j.second) < (i.first - j.first) * (i.second - k.second)); } void insert(const int x, const int y) { while (Q[x].size() > 1) { int i = Q[x].back(); Q[x].pop_back(); int j = Q[x].back(); if (judge(j, i, y)) { Q[x].push_back(i); break; } } Q[x].push_back(y); } int query(const int p) { int x = MU[p].y; while (Q[x].size() > 1) { int i = Q[x].front(); Q[x].pop_front(); int j = Q[x].front(); auto s = calc(i), t = calc(j); ll k = ((A * MU[p].q) << 1); if ((k * (s.second - t.second)) > (s.first - t.first)) { Q[x].push_front(i); break; } } return Q[x].size () ? Q[x].front() : 0; }