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;
}

细节细节细节!!!

posted @ 2021-04-06 20:46  lahlah  阅读(30)  评论(0编辑  收藏  举报