【NOI2014】购票(斜率优化dp+树链剖分)

http://uoj.ac/problem/7

\(dis[i]\)为到根的距离。

dp就是:
\(f[i]=min(f[j]+(dis[i]-dis[j])*p[i]+q[i])\)

可行的\(j\)\(i\)到一个祖先的一条链。

考虑树是一条链的时候,直接扫过去并用线段树维护下凸壳,查询就线段树上每个区间二分即可。

树上时我用了树链剖分,即把到根路径分成\(log\)段连续重链。

暴力就是线段树维护凸壳,时间复杂度:\(O(n~log^3~n)\)

除了最上面的那段,都是一段重链的前缀,那么在dp时,先走轻儿子,再走重儿子,维护当前这条重链的前缀的凸壳,即可一个\(log\)查询。

最上面那条还是要线段树查询,时间复杂度:\(O(n~log^2~n)\)

凸壳用vector存,封装好加入和查询的操作,挺好写的。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 2e5 + 5;

int n, tp;

int fa[N]; ll fv[N], s[N], p[N], q[N], l[N];

vector<int> e[N];
#define pb push_back
#define si size()

int siz[N], son[N], w[N], top[N];

void dg(int x) {
	s[x] = s[fa[x]] + fv[x];
	siz[x] = 1;
	ff(_y, 0, e[x].si) {
		int y = e[x][_y];
		dg(y);
		siz[x] += siz[y];
		if(siz[y] > siz[son[x]]) son[x] = y;
	}
}

int ed[N];

void dfs(int x) {
	if(!top[x]) top[x] = x;
	ed[top[x]] = x;
	w[x] = top[x] == x ? 1 : w[fa[x]] + 1;
	if(son[x]) top[son[x]] = top[x], dfs(son[x]);
	ff(_y, 0, e[x].si) {
		int y = e[x][_y]; if(y == son[x]) continue;
		dfs(y);
	}
}

#define db double
struct P {
	ll x, y;
	P(ll _x = 0, ll _y = 0) {
		x = _x, y = _y;
	}
};
db jd(P a, P b) {
	return (db) (a.y - b.y) / (b.x - a.x);
}
ll dot(P a, ll x) {
	return a.x * x + a.y;
}
int cmp(P a, P b) { return a.x < b.x;}
void add(vector<P> &a, P b) {
	while(a.si > 1 && jd(a[a.si - 2], a[a.si - 1]) <= jd(a[a.si - 1], b)) a.resize(a.si - 1);
	a.pb(b);
}
ll qry(vector<P> &a, ll x) {
	ll ans = 1e18;
	if(a.si == 0) return 1e18;
	int sz = a.si;
	int as = sz - 1;
	for(int l = 0, r = sz - 2; l <= r; ) {
		int m = l + r >> 1;
		if(jd(a[m], a[m + 1]) <= x) as = m, r = m - 1; else l = m + 1;
	}
	return dot(a[as], x);
}

int rt[N], tt;
int pl, pr;
ll px, py;

namespace tr {
	#define i0 t[i].l
	#define i1 t[i].r
	struct tree {
		int l, r;
	} t[N * 4];
	vector<P> a[N * 4];
	void add(int &i, int x, int y) {
		if(y < pl || x > pr) return;
		if(!i) i = ++ tt;
		add(a[i], P(px, py));
		if(x == y) return;
		int m = x + y >> 1;
		add(i0, x, m); add(i1, m + 1, y);
	}
	void ft(int &i, int x, int y) {
		if(y < pl || x > pr || !i) return;
		if(x >= pl && y <= pr) {
			px = min(px, qry(a[i], py));
			return;
		}
		int m = x + y >> 1;
		ft(i0, x, m); ft(i1, m + 1, y);
	}
}

ll f[N];

int z[N], z0;

int ef(ll L) {
	int as = z0 - 1;
	for(int l = 1, r = z0 - 1; l <= r; ) {
		int m = l + r >> 1;
		if(s[z[z0]] - s[z[m]] <= L) as = m, r = m - 1; else l = m + 1;
	}
	return z[as];
}

vector<P> b[N];

void work(int t, int x, int y) {
	while(top[x] != top[y]) {
		f[t] = min(f[t], qry(b[top[x]], -p[t]));
		x = fa[top[x]];
	}
	pl = w[y], pr = w[x]; px = 1e18; py = -p[t];
	int z = top[x];
	tr :: ft(rt[z], 1, w[ed[z]]);
	f[t] = min(f[t], px);
}

void du(int x) {
	z[++ z0] = x;
	
	if(x != 1) {
		int z = ef(l[x]);
		f[x] = 1e18;
		work(x, fa[x], z);
		f[x] += q[x] + s[x] * p[x];
	}
	
	add(b[top[x]], P(s[x], f[x]));
	pl = pr = w[x]; px = s[x]; py = f[x];
	tr :: add(rt[top[x]], 1, w[ed[top[x]]]);
	
	ff(_y, 0, e[x].si) {
		int y = e[x][_y]; if(y == son[x]) continue;
		du(y);
	}
	if(son[x]) du(son[x]);
	
	z0 --;
}

int main() {
	scanf("%d %d", &n, &tp);
	fo(i, 2, n) {
		scanf("%d %lld %lld %lld %lld", &fa[i], &fv[i], &p[i], &q[i], &l[i]);
		e[fa[i]].pb(i);
	}
	dg(1); dfs(1);
	du(1);
	fo(i, 2, n) pp("%lld\n", f[i]); hh;
}
posted @ 2020-04-30 15:26  Cold_Chair  阅读(181)  评论(0编辑  收藏  举报