【NOI2014】购票(斜率优化dp+树链剖分)
记\(dis[i]\)为到根的距离。
dp就是:
\(f[i]=min(f[j]+(dis[i]-dis[j])*p[i]+q[i])\)
可行的\(j\)是\(i\)到一个祖先的一条链。
考虑树是一条链的时候,直接扫过去并用线段树维护下凸壳,查询就线段树上每个区间二分即可。
树上时我用了树链剖分,即把到根路径分成\(log\)段连续重链。
暴力就是线段树维护凸壳,时间复杂度:\(O(n~log^3~n)\)
除了最上面的那段,都是一段重链的前缀,那么在dp时,先走轻儿子,再走重儿子,维护当前这条重链的前缀的凸壳,即可一个\(log\)查询。
最上面那条还是要线段树查询,时间复杂度:\(O(n~log^2~n)\)
凸壳用vector存,封装好加入和查询的操作,挺好写的。
Code:
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i < _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;
const int N = 2e5 + 5;
int n, tp;
int fa[N]; ll fv[N], s[N], p[N], q[N], l[N];
vector<int> e[N];
#define pb push_back
#define si size()
int siz[N], son[N], w[N], top[N];
void dg(int x) {
s[x] = s[fa[x]] + fv[x];
siz[x] = 1;
ff(_y, 0, e[x].si) {
int y = e[x][_y];
dg(y);
siz[x] += siz[y];
if(siz[y] > siz[son[x]]) son[x] = y;
}
}
int ed[N];
void dfs(int x) {
if(!top[x]) top[x] = x;
ed[top[x]] = x;
w[x] = top[x] == x ? 1 : w[fa[x]] + 1;
if(son[x]) top[son[x]] = top[x], dfs(son[x]);
ff(_y, 0, e[x].si) {
int y = e[x][_y]; if(y == son[x]) continue;
dfs(y);
}
}
#define db double
struct P {
ll x, y;
P(ll _x = 0, ll _y = 0) {
x = _x, y = _y;
}
};
db jd(P a, P b) {
return (db) (a.y - b.y) / (b.x - a.x);
}
ll dot(P a, ll x) {
return a.x * x + a.y;
}
int cmp(P a, P b) { return a.x < b.x;}
void add(vector<P> &a, P b) {
while(a.si > 1 && jd(a[a.si - 2], a[a.si - 1]) <= jd(a[a.si - 1], b)) a.resize(a.si - 1);
a.pb(b);
}
ll qry(vector<P> &a, ll x) {
ll ans = 1e18;
if(a.si == 0) return 1e18;
int sz = a.si;
int as = sz - 1;
for(int l = 0, r = sz - 2; l <= r; ) {
int m = l + r >> 1;
if(jd(a[m], a[m + 1]) <= x) as = m, r = m - 1; else l = m + 1;
}
return dot(a[as], x);
}
int rt[N], tt;
int pl, pr;
ll px, py;
namespace tr {
#define i0 t[i].l
#define i1 t[i].r
struct tree {
int l, r;
} t[N * 4];
vector<P> a[N * 4];
void add(int &i, int x, int y) {
if(y < pl || x > pr) return;
if(!i) i = ++ tt;
add(a[i], P(px, py));
if(x == y) return;
int m = x + y >> 1;
add(i0, x, m); add(i1, m + 1, y);
}
void ft(int &i, int x, int y) {
if(y < pl || x > pr || !i) return;
if(x >= pl && y <= pr) {
px = min(px, qry(a[i], py));
return;
}
int m = x + y >> 1;
ft(i0, x, m); ft(i1, m + 1, y);
}
}
ll f[N];
int z[N], z0;
int ef(ll L) {
int as = z0 - 1;
for(int l = 1, r = z0 - 1; l <= r; ) {
int m = l + r >> 1;
if(s[z[z0]] - s[z[m]] <= L) as = m, r = m - 1; else l = m + 1;
}
return z[as];
}
vector<P> b[N];
void work(int t, int x, int y) {
while(top[x] != top[y]) {
f[t] = min(f[t], qry(b[top[x]], -p[t]));
x = fa[top[x]];
}
pl = w[y], pr = w[x]; px = 1e18; py = -p[t];
int z = top[x];
tr :: ft(rt[z], 1, w[ed[z]]);
f[t] = min(f[t], px);
}
void du(int x) {
z[++ z0] = x;
if(x != 1) {
int z = ef(l[x]);
f[x] = 1e18;
work(x, fa[x], z);
f[x] += q[x] + s[x] * p[x];
}
add(b[top[x]], P(s[x], f[x]));
pl = pr = w[x]; px = s[x]; py = f[x];
tr :: add(rt[top[x]], 1, w[ed[top[x]]]);
ff(_y, 0, e[x].si) {
int y = e[x][_y]; if(y == son[x]) continue;
du(y);
}
if(son[x]) du(son[x]);
z0 --;
}
int main() {
scanf("%d %d", &n, &tp);
fo(i, 2, n) {
scanf("%d %lld %lld %lld %lld", &fa[i], &fv[i], &p[i], &q[i], &l[i]);
e[fa[i]].pb(i);
}
dg(1); dfs(1);
du(1);
fo(i, 2, n) pp("%lld\n", f[i]); hh;
}
转载注意标注出处:
转自Cold_Chair的博客+原博客地址