luogu P2305 [NOI2014] 购票
https://www.luogu.com.cn/problem/P2305
首先考虑无距离限制的转移方程
f
[
u
]
=
m
i
n
{
f
[
v
]
+
(
d
i
s
[
u
]
−
d
i
s
[
v
]
)
×
p
[
u
]
+
q
[
u
]
}
f[u]=min\{f[v]+(dis[u]-dis[v])\times p[u] + q[u]\}
f[u]=min{f[v]+(dis[u]−dis[v])×p[u]+q[u]}
f
[
u
]
=
m
i
n
{
f
[
v
]
−
d
i
s
[
v
]
×
p
[
u
]
+
d
i
s
[
u
]
×
p
[
u
]
+
q
[
u
]
}
f[u]=min\{f[v]-dis[v]\times p[u] +dis[u]\times p[u] + q[u]\}
f[u]=min{f[v]−dis[v]×p[u]+dis[u]×p[u]+q[u]}
同样先把min丢了
f
[
u
]
=
f
[
v
]
−
d
i
s
[
v
]
×
p
[
u
]
+
d
i
s
[
u
]
×
p
[
u
]
+
q
[
u
]
f[u]=f[v]-dis[v]\times p[u] +dis[u]\times p[u] + q[u]
f[u]=f[v]−dis[v]×p[u]+dis[u]×p[u]+q[u]
f
[
v
]
=
d
i
s
[
v
]
×
p
[
u
]
+
f
[
u
]
−
d
i
s
[
u
]
×
p
[
u
]
−
q
[
u
]
f[v]=dis[v] \times p[u] + f[u] - dis[u]\times p[u] - q[u]
f[v]=dis[v]×p[u]+f[u]−dis[u]×p[u]−q[u]
斜率优化的点就是
(
d
i
s
[
v
]
,
f
[
v
]
)
(dis[v],f[v])
(dis[v],f[v]),斜率为
p
[
u
]
p[u]
p[u]
然而这样并不是很好写,真正写起来是把两端路径拼起来的
所以方程要改一下
f [ u ] = m i n { f [ v ] + ( d i s [ u ] + d i s [ v ] ) × p [ u ] + q [ u ] } f[u]=min\{f[v]+(dis[u]+dis[v])\times p[u] + q[u]\} f[u]=min{f[v]+(dis[u]+dis[v])×p[u]+q[u]}
f
[
v
]
=
d
i
s
[
v
]
×
−
p
[
u
]
+
f
[
u
]
−
d
i
s
[
u
]
×
p
[
u
]
−
q
[
u
]
f[v]=dis[v] \times -p[u] + f[u] - dis[u]\times p[u] - q[u]
f[v]=dis[v]×−p[u]+f[u]−dis[u]×p[u]−q[u]
大力点分治+cdq分治
注意cdq分治的点和点分治的点是同一个
也就是考虑重心往上的路径对以重心为根的子树的贡献
具体看代码吧
code:
#include<bits/stdc++.h>
#define N 400005
#define ll long long
#define db long double
using namespace std;
struct edge {
int v, nxt;
} e[N << 1];
int p[N], eid;
void init() {
memset(p, -1, sizeof p);
eid = 0;
}
void add(int u, int v) {
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
struct PP {
ll x, y;
} sta[N], ls[N];
int size[N], msize[N], vis[N], tot, top, root, fa[N], sz;
ll d[N], l[N], s[N], x[N], y[N], f[N];
void dfs(int u) {
size[u] = 1; msize[u] = 0;
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(vis[v]) continue;
dfs(v); size[u] += size[v]; msize[u] = max(msize[u], size[v]);
}
if(!tot || max(msize[u], tot - size[u]) < max(msize[root], tot - size[root])) root = u;
}
void dfss(int u) {
if(l[u] - d[u] > 0) ls[++ sz] = (PP){l[u] - d[u], u};
for(int i = p[u]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(vis[v]) continue;
d[v] = d[u] + s[v]; dfss(v);
}
}
int cmp(PP x, PP y) {
return x.x < y.x;
}
db slope(PP x, PP y) { return (db)(x.y - y.y) / (x.x - y.x); }
db sl[N];
void insert(PP a) {
while(top && slope(sta[top], a) <= sl[top]) top --;
sta[++ top] = a;
sl[top] = top > 1? slope(sta[top - 1], sta[top]) : -1e18;
}
ll query(ll k) { printf("*%lld*", k);
int l = 0, r = top + 1;
while(l + 1 < r) {
int mid = (l + r) >> 1;
if(sl[mid] <= k) l = mid;
else r = mid;
}
return sta[l].y - k * sta[l].x;
}
void solve(int u) {
root = 0; dfs(u); vis[root] = 1;
int wrt = root;
if(root != u) tot -= size[root], solve(u);
int v = wrt;
ll dis = 0;
sz = top = d[wrt] = 0; dfss(wrt);
sort(ls + 1, ls + 1 + sz, cmp);
for(int i = 1; i <= sz; i ++) {
ll lim = ls[i].x, id = ls[i].y;
while(v != fa[u] && fa[v] && dis + s[v] <= lim)
insert((PP){dis += s[v], f[fa[v]]}), v = fa[v];
if(top) f[id] = min(f[id], query(-x[id]) + d[id] * x[id] + y[id]);
}
for(int i = 1; i <= top; i ++) cout << sl[i] << ' '; printf("\n");
for(int i = p[wrt]; i + 1; i = e[i].nxt) {
int v = e[i].v;
if(!vis[v]) tot = size[v], solve(v);
}
}
int n, t;
int main() { init();
scanf("%d%d", &n, &t);
for(int i = 2; i <= n; i ++) {
scanf("%d%lld%lld%lld%lld", &fa[i], &s[i], &x[i], &y[i], &l[i]);
add(fa[i], i), f[i] = 1e18;
}
tot = n, vis[0] = 1; solve(1);
for(int i = 2; i <= n; i ++) printf("%lld\n", f[i]);
return 0;
}
细节细节细节!!!