【NOI2014】【cdq点分治】【斜率优化】购票

3 篇文章 0 订阅
2 篇文章 0 订阅

0.先推出斜率优化的动归方程

1.找到当前分治的树结构的重心

2.将分成的子树中含有根节点那部分连重心一并分治

3.将其余子树的点拎出来,按照能走到的最小深度从大到小排序

4.对于每个点,将重心到分治结构的根节点路径上所有的点中能到达的那些点维护一个凸包 然后二分查找

5.对其余子树进行分治

时间复杂度O(nlog^2n)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
typedef long double DB;
const int N = 500005;
const LL inf = 1LL << 62;
struct Edge { int y, nex; LL z; } g[N];
struct br { int n; LL z; } b[N];
struct CvH { LL x, y; } q[N];
int n, pos[N], sz, don[N], rt, siz, s[N], f0[N], hd, tl, h[N], par[N], bz, up, qx, t;
LL u[N], l[N], f[N], q0[N], s1[N], Ds[N], p[N];
void Init(int x, int y, LL z) {
	g[++ sz].y = y;
	g[sz].z = z, g[sz].nex = pos[x];
	pos[x] = sz;
}
void Pred(int x) {
	RepE(i, x) {
		int y = g[i].y;
		if (y == par[x]) continue ;
		h[y] = h[x] + 1, par[y] = x, s1[y] = s1[x] + g[i].z;
		Pred(y);
	}
}
void Get(int x, int ft) {
	s[x] = 1, f0[x] = 0;
	RepE(i, x) {
		int y = g[i].y;
		if (don[y] || y == ft) continue ;
		Get(y, x), s[x] += s[y], f0[x] = max(f0[x], s[y]);
	}
	f0[x] = max(f0[x], siz - s[x]);
	if (f0[x] < f0[rt] || (f0[x] == f0[rt] && h[x] < h[rt])) rt = x;
}
void Dfs(int x, LL z) {
	if (l[x] >= z) b[++ bz] = (br) { x, l[x] - z };
	RepE(i, x) {
		int y = g[i].y;
		if (y != par[x] && !don[y]) Dfs(y, z + g[i].z);
	}
}
void Find(int x, int ft) {
	if (h[x] < h[up]) up = x;
	RepE(i, x) {
		int y = g[i].y;
		if (y != ft && !don[y]) Find(y, x);
	}
}
bool Cmpb(br x, br y) { return x.z < y.z; }
void Work(int x) {
	bool fl = 0;
	RepE(i, x) if (!don[ g[i].y ] && g[i].y != par[x]) don[ g[i].y ] = 2, fl = 1;
	qx = x, up = x, Find(x, 0);
	int up1 = up;
	if (!don[ par[x] ] && x != 1) {
		siz = s[ par[x] ] + 1, Get(par[x], rt = 0);
		Work(rt);
	} else don[x] = 1;
	bz = 0;
	RepE(i, x) {
		int y = g[i].y;
		if (don[y] == 2 && fl) don[y] = 0; else continue ;
		Dfs(y, g[i].z);
	}
	sort(b + 1, b + bz + 1, Cmpb);
	int k = x; LL t0 = 0;
	q[hd = tl = n] = (CvH) { s1[x], f[x] };
	Rep(i, 1, bz) {
		while (t0 + Ds[k] <= b[i].z && k != up1) {
			t0 += Ds[k], k = par[k];
			while (hd < tl && (q[hd + 1].y - q[hd].y) / (DB)(q[hd + 1].x - q[hd].x) <= (q[hd].y - f[k]) / (DB)(q[hd].x - s1[k])) hd ++;
			q[-- hd] = (CvH) { s1[k], f[k] };
		}
		int l = hd, r = tl;
		while (l < r) {
			int u = (l + r + 1) >> 1;
			if ((q[u].y - q[u - 1].y) / (DB)(q[u].x - q[u - 1].x) < p[ b[i].n ]) l = u;
			else r = u - 1;
		}
		f[ b[i].n ] = min(f[ b[i].n ], q[l].y + (s1[ b[i].n ] - q[l].x) * p[ b[i].n ] + q0[ b[i].n ]);
	}
	RepE(i, x) {
		int y = g[i].y;
		if (don[y]) continue ;
		siz = s[y];
		Get(y, rt = 0), Work(rt);
	}
}
int main()
{
	scanf ("%d%d", &n, &t);
	Rep(i, 2, n) {
		int x;
		scanf ("%d%lld%lld%lld%lld", &x, &Ds[i], &p[i], &q0[i], &l[i]);
		Init(x, i, Ds[i]), Init(i, x, Ds[i]);
		f[i] = inf;
	}
	Pred(1);
	siz = n, f0[0] = n + 1;
	Get(1, rt = 0), Work(rt);
	Rep(i, 2, n) printf("%lld\n", f[i]);
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值