Bzoj3672: [Noi2014]购票
题面
Sol
设\(f[i]\)表示\(i\)到根的最小代价
\(f[i]\)可以由\(f[j]\)转移而来,要求\(j\)为\(i\)的父亲,并且满足距离限制
显然\(DP\)式可以斜率优化
然而这是在树上,并且每次都要一个\(i\)往上的若干个点的凸包
可以考虑维护区间凸包,可以用线段树
或者\(CDQ\)分治
\(CDQ\)分治的方法,其实是点分治,每次先解决重心到其祖先那一块
然后考虑它们对其它子树的贡献,区间凸包可以转化为后缀凸包
排序后每次在前面加入并查询即可
# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
IL ll Input(){
RG ll x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
const int maxn(2e5 + 5);
int n, first[maxn], cnt;
int vis[maxn], size[maxn], mx[maxn], rt, sz, fa[maxn];
int q1[maxn], q2[maxn], l1, r1, len, anc[maxn], num;
ll deep[maxn], s[maxn], f[maxn], l[maxn], q[maxn], p[maxn];
struct Edge{
int to, next;
ll w;
} edge[maxn << 1];
IL void Add(RG int u, RG int v, RG ll w){
edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
}
IL void Dfs(RG int u, RG int ff){
s[u] = deep[u] * p[u] + q[u], l[u] = deep[u] - l[u];
for(RG int e = first[u]; e != -1; e = edge[e].next){
RG int v = edge[e].to;
if(v != ff){
deep[v] = deep[u] + edge[e].w;
fa[v] = u, Dfs(v, u);
}
}
}
IL void GetRoot(RG int u, RG int ff){
size[u] = 1, mx[u] = 0;
for(RG int e = first[u]; e != -1; e = edge[e].next){
RG int v = edge[e].to;
if(v != ff && !vis[v]){
GetRoot(v, u);
size[u] += size[v];
mx[u] = max(mx[u], size[v]);
}
}
mx[u] = max(mx[u], sz - size[u]);
if(mx[u] < mx[rt]) rt = u;
}
IL void GetSon(RG int u, RG int ff){
q2[++len] = u;
for(RG int e = first[u]; e != -1; e = edge[e].next){
RG int v = edge[e].to;
if(!vis[v] && v != ff) GetSon(v, u);
}
}
IL int Cmp(RG int x, RG int y){
return l[x] > l[y];
}
IL double Slope(RG int x, RG int y){
return 1.0 * (f[x] - f[y]) / (1.0 * (deep[x] - deep[y]));
}
IL void Solve(RG int last, RG int u){
vis[u] = 1;
for(RG int e = first[u]; e != -1; e = edge[e].next)
if(edge[e].to == fa[u] && !vis[edge[e].to]){
rt = 0, sz = size[edge[e].to];
GetRoot(edge[e].to, u), Solve(last, rt);
}
num = len = 0, GetSon(u, fa[u]);
for(RG int i = u; i != last; i = fa[i]) anc[++num] = fa[i];
l1 = 0, r1 = -1, sort(q2 + 1, q2 + len + 1, Cmp);
for(RG int i = 1, j = 1; i <= len; ++i){
while(j <= num && deep[anc[j]] >= l[q2[i]]){
while(l1 < r1 && Slope(q1[r1 - 1], q1[r1]) <= Slope(q1[r1 - 1], anc[j])) --r1;
q1[++r1] = anc[j++];
}
if(l1 <= r1){
RG int ql = l1 + 1, qr = r1, pos = l1;
while(ql <= qr){
RG int mid = (ql + qr) >> 1;
if(Slope(q1[mid - 1], q1[mid]) > p[q2[i]]) pos = mid, ql = mid + 1;
else qr = mid - 1;
}
f[q2[i]] = min(f[q2[i]], f[q1[pos]] + s[q2[i]] - deep[q1[pos]] * p[q2[i]]);
}
}
for(RG int e = first[u]; e != -1; e = edge[e].next)
if(edge[e].to != fa[u] && !vis[edge[e].to]){
rt = 0, sz = size[edge[e].to];
GetRoot(edge[e].to, u), Solve(u, rt);
}
}
int main(){
n = Input(), Input();
for(RG int i = 1; i <= n; ++i) first[i] = -1, f[i] = 1e18;
mx[0] = n + 1, sz = n, f[1] = 0;
for(RG int i = 2; i <= n; ++i){
RG ll ff = Input(), w = Input();
Add(ff, i, w), Add(i, ff, w);
p[i] = Input(), q[i] = Input(), l[i] = Input();
}
Dfs(1, 0), GetRoot(1, 0), Solve(1, rt);
for(RG int i = 2; i <= n; ++i) printf("%lld\n", f[i]);
return 0;
}