BZOJ3672: [Noi2014]購票【CDQ分治】【點分治】【斜率優化DP】

Description

今年夏天,NOI在SZ市迎來了她30週歲的生日。來自全國 n 個城市的OIer們都會從各地出發,到SZ市參加此次盛會。c++

全國的城市構成了一棵以SZ市爲根的有根樹,每一個城市與它的父親用道路鏈接。爲了方便起見,咱們將全國的 n 個城市用 1 到 n 的整數編號。其中SZ市的編號爲 1。對於除SZ市以外的任意一個城市 v,咱們給出了它在這棵樹上的父親城市 fv 以及到父親城市道路的長度 sv。git

從城市 v 前往SZ市的方法爲:選擇城市 v 的一個祖先 a,支付購票的費用,乘坐交通工具到達 a。再選擇城市 a 的一個祖先 b,支付費用併到達 b。以此類推,直至到達SZ市。算法

對於任意一個城市 v,咱們會給出一個交通工具的距離限制 lv。對於城市 v 的祖先 a,只有當它們之間全部道路的總長度不超過 lv 時,從城市 v 才能夠經過一次購票到達城市 a,不然不能經過一次購票到達。對於每一個城市 v,咱們還會給出兩個非負整數 pv,qv 做爲票價參數。若城市 v 到城市 a 全部道路的總長度爲 d,那麼從城市 v 到城市 a 購買的票價爲 dpv+qv。工具

每一個城市的OIer都但願本身到達SZ市時,用於購票的總資金最少。你的任務就是,告訴每一個城市的OIer他們所花的最少資金是多少。測試

Input

第 1 行包含2個非負整數 n,t,分別表示城市的個數和數據類型(其意義將在後面提到)。輸入文件的第 2 到 n 行,每行描述一個除SZ以外的城市。其中第 v 行包含 5 個非負整數 f_v,s_v,p_v,q_v,l_v,分別表示城市 v 的父親城市,它到父親城市道路的長度,票價的兩個參數和距離限制。請注意:輸入不包含編號爲 1 的SZ市,第 2 行到第 n 行分別描述的是城市 2 到城市 n。優化

Output

輸出包含 n-1 行,每行包含一個整數。其中第 v 行表示從城市 v+1 出發,到達SZ市最少的購票費用。一樣請注意:輸出不包含編號爲 1 的SZ市。spa

Sample Input

7 3
1 2 20 0 3
1 5 10 100 5
2 4 10 10 10
2 9 1 100 10
3 5 20 100 10
4 4 20 0 10code

Sample Output

40
150
0
149
300
150blog

HINT

img

對於全部測試數據,保證 0≤pv≤106,0≤qv≤1012,1≤fv<v;保證 0<sv≤lv≤2×1011,且任意城市到SZ市的總路程長度不超過 2×1011。遞歸

輸入的 t 表示數據類型,0≤t<4,其中:

當 t=0 或 2 時,對輸入的全部城市 v,都有 fv=v-1,即全部城市構成一個以SZ市爲終點的鏈;

當 t=0 或 1 時,對輸入的全部城市 v,都有 lv=2×1011,即沒有移動的距離限制,每一個城市都能到達它的全部祖先;

當 t=3 時,數據沒有特殊性質。

n=2×10^5


思路

經典好題吧

知識點很全

首先考慮在一條鏈上怎麼作?

斜率優化是一眼的

可是在樹上怎麼辦?

咱們要用一個節點的全部父親來更新這個節點的dp值

考慮下分治算法來優化這個過程

在斜率優化的處理方式中有一種經典操做叫cdq分治

就是先處理一部分而後用這一部分更新剩下的部分,而後再遞歸處理剩下的部分

這裏咱們把問題模型抽象出來

若是把樹劃分紅幾個部分的話,咱們考慮一個事情,就是說用來更新的祖先是一條鏈,能夠更新到的兒子是一個子樹

咱們把祖先的鏈當成前一個部分,兒子當作另外一個部分

也就是說咱們先處理出前一個部分的全部信息,而後再用來更新兒子

考慮分治的過程

當前分治的樹根是u,分治中心是rt

那麼顯然\([rt,u]\)這條鏈上的信息咱們須要先處理出來

因此咱們優先遞歸除了rt子樹外的全部節點

而後再把\([rt,u]\)這一條鏈提出來更新rt的子樹

注意由於有一個最大長度限制

因此咱們進行更新的時候必定要注意把子樹節點按照必定順序排好,使得能夠更新每個節點的祖先數量單調不降

而後就對祖先維護凸殼就行了


注意一下點積不要爆longlong,用longdouble

還有找重心的時候若是當前節點siz是1須要特判一下不能做爲重心


#include<bits/stdc++.h>

using namespace std;

typedef long double ld;
typedef long long ll;

ll read() {
  ll res = 0, w = 1; char c = getchar();
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-') c = getchar(), w = -1;
  while (isdigit(c)) res = (res << 1) + (res << 3) + c - '0', c = getchar();
  return w * res;
}

const ll INF_of_ll = 1e18;
const ll N = 2e5 + 10; 

struct Node {
  ll id, val;
  Node() {}
  Node(ll id, ll val): id(id), val(val) {}
} rque[N];
 
bool operator < (const Node &a, const Node &b) {
  return a.val > b.val;
}

struct Vector {
  ll x, y;
  Vector() {}
  Vector(ll x, ll y): x(x), y(y) {}
} lque[N];

Vector operator - (const Vector &a, const Vector &b) {
  return Vector(a.x - b.x, a.y - b.y);
}

ld operator * (const Vector &a, const Vector &b) {
  return (ld) a.x * b.y - (ld) a.y * b.x;
}

struct Edge {
  ll v, w, nxt;
} E[N << 1];

ll topl, topr;
ll head[N], tot = 0;
ll n, prt[N], p[N], q[N], limit[N], dp[N];
ll siz[N], dis[N], F[N], vis[N], siz_all;

void addedge(ll u, ll v, ll w) {
  E[++tot] = (Edge) {v, w, head[u]};
  head[u] = tot;
}

void getsiz(ll u) {
  siz[u] = 1;
  for (ll i = head[u]; i; i = E[i].nxt) {
    ll v = E[i].v;
    if (vis[v]) continue;
    dis[v] = dis[u] + E[i].w;
    getsiz(v);
    siz[u] += siz[v];
  }
}

void getrt(ll u, ll &rt) {
  F[u] = siz_all - siz[u];
  for (ll i = head[u]; i; i = E[i].nxt) {
    ll v = E[i].v;
    if (vis[v]) continue;
    getrt(v, rt);
    F[u] = max(F[u], siz[v]);
  }
  if (F[u] < F[rt] && siz[u] > 1) rt = u;
} 

void dfs(ll u) {
  rque[++topr] = (Node) {u, dis[u] - limit[u]};
  for (ll i = head[u]; i; i = E[i].nxt) 
    if (!vis[E[i].v]) dfs(E[i].v);
}

ll calc(ll v, Vector u) {
  return u.y + (dis[v] - u.x) * p[v] + q[v];
}

void solve(ll u, ll cursiz) {
  if (cursiz == 1) return;
  ll rt = 0;
  getsiz(u);
  F[rt] = siz_all = cursiz;
  getrt(u, rt);
  for (ll i = head[rt]; i; i = E[i].nxt) 
    vis[E[i].v] = 1;
  solve(u, cursiz - siz[rt] + 1);
  topl = topr = 0;
  for (ll i = head[rt]; i; i = E[i].nxt) 
    dfs(E[i].v);
  sort(rque + 1, rque + topr + 1);
  ll cur = rt;
  lque[0] = Vector(dis[rt] + 1, INF_of_ll);
  for (ll i = 1; i <= topr; i++) {
    while (cur != prt[u] && dis[cur] >= rque[i].val) {
      Vector now(dis[cur], dp[cur]);
      while (topl > 1 && (lque[topl] - lque[topl - 1]) * (now - lque[topl - 1]) > 0.0) topl--;
      lque[++topl] = now;
      cur = prt[cur];
    }
    if (topl) {
      ll l = 1, r = topl, res = 1, now = rque[i].id;
      while (l <= r) {
        ll mid = (l + r) >> 1;
        if (calc(now, lque[mid]) <= calc(now, lque[mid - 1])) res = mid, l = mid + 1;
        else r = mid - 1;
      }
      dp[now] = min(dp[now], calc(now, lque[res]));
    }
  }
  for (ll i = head[rt]; i; i = E[i].nxt)
    solve(E[i].v, siz[E[i].v]);
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif 
  n = read();
  ll typ = read();
  for (ll i = 2; i <= n; i++) {
    prt[i] = read();
    ll w = read();
    addedge(prt[i], i, w);
    p[i] = read();
    q[i] = read();
    limit[i] = read();
    dp[i] = INF_of_ll;
  }
  solve(1, n);
  for (ll i = 2; i <= n; i++)
    printf("%lld\n", dp[i]); 
  return 0;
}
相關文章
相關標籤/搜索