题目:
题解:
这个很明显要从上往下DP,而且下面的DP选择了就可以直接选择用上面的信息直接转移了。
大概就是 f[i]=min(p[i]∗(dis[i]−dis[j])+q[i]+f[j]) f [ i ] = m i n ( p [ i ] ∗ ( d i s [ i ] − d i s [ j ] ) + q [ i ] + f [ j ] ) (dis[i]-dis[j]<=l[i],j=fa[j])
dis[i]当然是距离数组,f[i]就是答案数组啦
按照无关的量放在外面画柿子吧,
f[i]=min(−p[i]∗dis[j]+f[j])+p[i]∗dis[i]+q[i]
f
[
i
]
=
m
i
n
(
−
p
[
i
]
∗
d
i
s
[
j
]
+
f
[
j
]
)
+
p
[
i
]
∗
d
i
s
[
i
]
+
q
[
i
]
那么对于一个x=p[i]的情况,k=-dis[j],b=f[j],转化为y坐标最小。这样对于fa[i]到pre[i](最高满足条件的祖先),我们有这些直线,这些直线肯定不会都有用啊,其实是要维护一个下凸壳,然后选择出在横坐标为p[i]的情况下最小值所在的直线就ok了。
假设我们已经有一个半平面和横坐标x,那么我们可以在O(logn)的时间内找到使得y最小的那条线(三分)
既然是树,那么我们可以考虑一下树链剖分,每条树链用一个线段树维护一下树链上各个区间半平面交的结果,由于从祖先到子孙的dis[i]会递增,所以我们有一个天然的斜率顺序
所以这个可以用vector维护。
代码:
因为要会考就先咕了代码叭QAQ
先弄个正确的代码上来叭
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define ls x << 1
#define rs x << 1 | 1
#define eb emplace_back
#define pb pop_back
#define db double
const int MAX = 210033;
const ll inf = (1ll << 63) - 1;
struct Point
{
ll x, y;
Point() {}
Point(ll a, ll b) { x = a, y = b; }
friend Point operator - (Point a, Point b) { return Point(a.x - b.x, a.y - b.y); }
friend ll operator * (Point a, Point b) { return a.x * b.y - a.y * b.x; }
};
struct edge
{
int to; ll w;
edge() {}
edge(int a, ll b) { to = a, w = b; }
};
vector<edge> e[MAX];
vector<Point> h[MAX << 2];
int n, cnt_dfn;
int dfn[MAX], top[MAX], inv[MAX], siz[MAX], fa[MAX], que[MAX];
ll ans[MAX], P[MAX], Q[MAX], lim[MAX], dis[MAX];
void dfs1(int u)
{
siz[u] = 1;
for (auto v : e[u])
{
dis[v.to] = dis[u] + v.w;
dfs1(v.to);
siz[u] += siz[v.to];
}
}
void dfs2(int u, int tp)
{
int big = 0;
top[u] = tp, dfn[u] = ++cnt_dfn, inv[cnt_dfn] = u;
for (auto v : e[u])
if (siz[v.to] > siz[big])
big = v.to;
if (!big) return;
dfs2(big, tp);
for (auto v : e[u])
if (v.to != big)
dfs2(v.to, v.to);
}
void bfs()
{
int head, tail;
que[head = tail = 1] = 1;
while (head <= tail)
{
int u = que[head++];
for (auto v : e[u])
que[++tail] = v.to;
}
}
void update(int x, int l, int r, int p, Point c)
{
int siz = 0;
while ((siz = h[x].size()) > 1 && (c - h[x][siz - 2]) * (h[x][siz - 1] - h[x][siz - 2]) > 0)
h[x].pb();
h[x].eb(c);
if (l >= r) return;
int mid = (l + r) >> 1;
if (p <= mid) update(ls, l, mid, p, c);
else update(rs, mid + 1, r, p, c);
}
ll query(int x, int l, int r, int L, int R, int id)
{
ll Ans = inf;
if (L <= l && r <= R)
{
int lx = 0, rx = h[x].size() - 1;
while (rx - lx > 3)
{
int m1 = lx + (rx - lx) / 3, m2 = rx - (rx - lx) / 3;
ll v1 = P[id] * (dis[id] - h[x][m1].x) + h[x][m1].y;
ll v2 = P[id] * (dis[id] - h[x][m2].x) + h[x][m2].y;
if (v1 <= v2) rx = m2;
else lx = m1;
}
for (int i = lx; i <= rx; i++)
Ans = min(Ans, P[id] * (dis[id] - h[x][i].x) + h[x][i].y + Q[id]);
return Ans;
}
int mid = (l + r) >> 1;
if (L <= mid) Ans = min(Ans, query(ls, l, mid, L, R, id));
if (R > mid) Ans = min(Ans, query(rs, mid + 1, r, L, R, id));
return Ans;
}
void calc(int L, int R, int id)
{
ll Ans = inf;
while (dis[top[R]] > dis[L])
{
Ans = min(Ans, query(1, 1, n, dfn[top[R]], dfn[R], id));
R = fa[top[R]];
}
Ans = min(Ans, query(1, 1, n, dfn[L], dfn[R], id));
ans[id] = Ans;
update(1, 1, n, dfn[id], Point(dis[id], ans[id]));
}
main()
{
scanf("%lld%lld", &n, &fa[MAX - 1]);
for (int i = 2; i <= n; i++)
{
ll w;
scanf("%lld%lld%lld%lld%lld", &fa[i], &w, &P[i], &Q[i], &lim[i]);
e[fa[i]].eb(edge(i, w));
}
dfs1(1), dfs2(1, 1), bfs();
update(1, 1, n, 1, Point(0, 0));
for (int i = 2; i <= n; i++)
{
int anc = que[i], dist = dis[anc] - lim[anc];
anc = fa[anc];
while (anc > 1 && dis[fa[top[anc]]] >= dist)
anc = fa[top[anc]];
if (anc > 1)
{
int l = dfn[top[anc]], r = dfn[anc];
while (l <= r)
{
int mid = (l + r) >> 1;
if (dis[inv[mid]] >= dist)
anc = inv[mid], r = mid - 1;
else l = mid + 1;
}
}
else anc = 1;
calc(anc, fa[que[i]], que[i]);
}
for (int i = 2; i <= n; i++)
printf("%lld\n", ans[i]);
return 0;
}