朴素 dp 大约就是 , 是 的祖先。这个式子可以斜率优化,在以 为横坐标, 为纵坐标的坐标系中,斜率为 。
我们应该用单调栈维护一个到根的树链。由于回溯的时候 不一定单调,取 的 dp 值时我们应该在完整的凸壳上二分。
插入决策点时,按照普通序列上的斜率优化弹栈(朴素)。很多人的问题是回溯还原栈状态的复杂度,其实你真正修改的值只有插入进去的位置,如果你没有使用 STL 的话弹栈的操作实际上是没有进行的,他们保留在栈中,只需要将栈顶指针还原即可,每次回溯的个数是 级别的。
实际插入决策点时,因为时间复杂度的问题需要二分插入位置。如果你以乘代除,注意开 __int128
。
#include<bits/stdc++.h>
#define cmin(x, y) x = std::min(x, y)
#define cmax(x, y) x = std::max(x, y)
#define fors(i, l, r, ...) for(int i = (l), REP##i = (r), ##__VA_ARGS__; i <= REP##i; ++i)
#define dfors(i, r, l, ...) for(int i = (r), REP##i = (l), ##__VA_ARGS__; i >= REP##i; --i)
typedef long long ll;
int n, q[100100], l = 1, r;
ll f[100100], v[100100], s[100100], d[100100];
std::vector<std::pair<int, ll>> e[100100];
void pre_dfs(const int x, int fa) {
for(const auto [y, w] : e[x]) if(y != fa) d[y] = d[x]+w,pre_dfs(y, x);
}
ll up(const int i, const int j) { return f[j]-f[i]; }
ll down(const int i, const int j) { return d[j]-d[i]; }
ll cal(const int i, const int j) { return f[j]+v[i]*(d[i]-d[j])+s[i]; }
int bisect_optimal_decision(const ll k) {
int l = ::l, r = ::r, res = l; l++;
for(int mid; l <= r;) {
mid = (l+r)>>1;
if(up(q[mid-1], q[mid]) <= (__int128)k*down(q[mid-1], q[mid])) l = mid+1,res = mid;
else r = mid-1;
}
return q[res];
}
int bisect_insert_position(const int x) {
int l = ::l, r = ::r, res = l; l++;
for(int mid; l <= r;) {
mid = (l+r)>>1;
if((__int128)up(q[mid-1], q[mid])*down(q[mid], x) <= (__int128)up(q[mid], x)*down(q[mid-1], q[mid])) l = mid+1,res = mid;
else r = mid-1;
}
return res;
}
void dp_dfs(const int x, const int fa) {
if(x != 1) f[x] = cal(x, bisect_optimal_decision(v[x]));
int _r = r, t, tt;
r = bisect_insert_position(x);
t = r+1,tt = q[r+1],q[++r] = x;
for(const auto [y, w] : e[x]) if(y != fa) dp_dfs(y, x);
q[t] = tt,r = _r;
}
signed main() {
std::ios_base::sync_with_stdio(0);
std::cin.tie(0);
std::cin >> n;
fors(i, 1, n-1, x, y, z) {
std::cin >> x >> y >> z;
e[x].emplace_back(y, z);
e[y].emplace_back(x, z);
}
fors(i, 2, n) std::cin >> s[i] >> v[i];
pre_dfs(1, 0),dp_dfs(1, 0);
fors(i, 2, n) std::cout << f[i] << " \n"[i == n];
return 0;
}