Address
Solution
设
dis[u]
d
i
s
[
u
]
表示点
u
u
到根的距离。
表示
u
u
到根的最小费用。
表示满足
v
v
是 的祖先,
dis[u]−dis[v]≤lu
d
i
s
[
u
]
−
d
i
s
[
v
]
≤
l
u
且
v
v
的深度最小的点 。
那么易得转移方程:
拆开括号并移项:
相当于找一条斜率为 pu p u 的直线过点 (dis[v],f[v]) ( d i s [ v ] , f [ v ] ) 使得这条直线的截距 f[u] f [ u ] 最小。
于是把每个决策看作点 (dis[u],f[u]) ( d i s [ u ] , f [ u ] ) ,在任意时刻( f[u] f [ u ] 待计算时)维护根到 u u 的父亲的路径上所有决策点构成的下凸壳。由于 没有单调性,故需要在凸壳上二分找最优决策点。 裆燃,这样的做法不对。
我们能够发现,此题主要的困难不是树结构,而在于转移条件 dis[u]−dis[v]≤lu d i s [ u ] − d i s [ v ] ≤ l u 。
这意味着,如果 u u 的一个祖先 在 1 1 到 的路径上不是凸壳上的点(不可能成为最优决策),它可能在 top[u] t o p [ u ] 到 father(u) f a t h e r ( u ) 的路径上成为凸壳上的点(可能成为最优决策)。
先假设 这不是一棵树,是一个序列, i i 能从 转移。 f[1]=0 f [ 1 ] = 0 。
可以 CDQ 分治。设现在要处理 f[l...r] f [ l . . . r ] , mid=⌊l+r2⌋ m i d = ⌊ l + r 2 ⌋ 。
(1)先处理 f[l...mid] f [ l . . . m i d ] 。(递归下去)。
(2)处理 f[l...mid] f [ l . . . m i d ] 对 f[mid+1...r] f [ m i d + 1... r ] 的影响。具体地,将 f[mid+1...r] f [ m i d + 1... r ] 按照 top t o p 从大到小排序( top t o p 大于 mid m i d 的忽略),然后从 mid m i d 到 l l 顺次将每个点加入凸壳,期间如果第 个点到第 mid m i d 个点已经加入凸壳并且存在一个 j∈[mid+1,r] j ∈ [ m i d + 1 , r ] 满足 top[j]=i t o p [ j ] = i 则在第 i i 个点到第 个点的凸壳上二分来更新 f[j] f [ j ] 的值。
(3)处理 f[mid+1...r] f [ m i d + 1... r ] 。(递归下去)。
把这种算法迁移到树上,就是——点分治!!!!!
(1)找到树的重心 r r 。
(2)递归处理 的子树(不含 r r )之外的所有点构成的连通子树。
(3)将 的子树内(不含 r r )的点中, top 的深度小于等于 r 的深度的所有点 按照 的深度从大到小排序。
(4)从 v v 开始往当前处理的连通子树的根处理,每走到一个点就把一个点加入凸壳。
(5)如果一个时候,维护了 到 x x 的凸壳且有 ,则凸壳上二分更新 f[u] f [ u ] 。
(6)枚举 v v 的每个子节点 ,往 w w 的子树递归。
复杂度 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Tree(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
using namespace std;
typedef long long ll;
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
inline ll readll()
{
ll res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, LogN = 20;
const ll INF = ((1ll << 62) - 1 << 1) + 1;
int n, fa[N][LogN], p[N], ecnt, nxt[N], adj[N], go[N], sze[N], maxs[N],
dep[N], top[N], tot, z[N], len, bel[N];
ll f[N], q[N], s[N], l[N], dis[N];
bool vis[N];
struct point
{
ll x, y;
} que[N];
inline bool comp(const int &a, const int &b)
{
return dep[top[a]] > dep[top[b]];
}
template <class T>
T Max(T a, T b) {return a > b ? a : b;}
template <class T>
T Min(T a, T b) {return a < b ? a : b;}
void add_edge(int u, int v)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
}
void dfs(int u)
{
dep[u] = dep[fa[u][0]] + 1;
int i;
For (i, 0, 16) fa[u][i + 1] = fa[fa[u][i]][i];
Tree(u) dis[v] = dis[u] + s[v], dfs(v);
}
int extr(int u, ll lim)
{
int i, v = u;
Rof (i, 17, 0)
if (fa[u][i] && dis[v] - dis[fa[u][i]] <= lim)
u = fa[u][i];
return u;
}
void dfs1(int u)
{
sze[u] = 1; maxs[u] = 0;
Tree(u) if (!vis[v])
dfs1(v), sze[u] += sze[v],
maxs[u] = Max(maxs[u], sze[v]);
}
void dfs2(int r, int u, int &G)
{
if (Max(maxs[u], sze[r] - sze[u]) < Max(maxs[G], sze[r] - sze[G]))
G = u;
Tree(u) if (!vis[v]) dfs2(r, v, G);
}
int calcG(int u)
{
int G = u;
dfs1(u); dfs2(u, u, G);
return G;
}
void dfs3(int rt, int u, int maxd)
{
if (dep[top[u]] <= maxd)
z[++tot] = u, bel[tot] = rt;
Tree(u) if (!vis[v]) dfs3(rt, v, maxd);
}
double slope(point a, point b)
{
return 1.0 * (a.y - b.y) / (a.x - b.x);
}
void convex_hull(point x)
{
while (len > 1 && slope(x, que[len]) >= slope(que[len], que[len - 1]))
len--;
que[++len] = x;
}
ll query(ll sl)
{
int l = 1, r = len - 1;
while (l <= r)
{
int mid = l + r >> 1;
ll tmp = (que[mid + 1].y - que[mid + 1].x * sl)
- (que[mid].y - que[mid].x * sl);
if (tmp >= 0) r = mid - 1;
else l = mid + 1;
}
return que[l].y - que[l].x * sl;
}
void zzqioirank1(int u)
{
int G = calcG(u); vis[G] = 1;
if (u != G)
{
zzqioirank1(u);
int v = fa[G][0];
while (dep[v] >= dep[u] && dep[v] >= dep[top[G]])
f[G] = Min(f[G], f[v] + (dis[G] - dis[v]) * p[G] + q[G]),
v = fa[v][0];
}
tot = len = 0;
Tree(G) if (!vis[v]) dfs3(v, v, dep[G]);
sort(z + 1, z + tot + 1, comp);
int i, v = G;
For (i, 1, tot)
{
int u = z[i];
while (dep[v] >= dep[top[u]])
convex_hull((point) {dis[v], f[v]}), v = fa[v][0];
f[u] = Min(f[u], query(p[u]) + dis[u] * p[u] + q[u]);
top[u] = bel[i];
}
Tree(G) if (!vis[v]) zzqioirank1(v);
}
int main()
{
int i;
n = read(); read();
For (i, 2, n)
{
f[i] = INF;
fa[i][0] = read(); s[i] = readll();
p[i] = read(); q[i] = readll();
l[i] = readll();
add_edge(fa[i][0], i);
}
dfs(1);
For (i, 1, n) top[i] = extr(i, l[i]);
zzqioirank1(1);
For (i, 2, n) printf("%lld\n", f[i]);
return 0;
}