ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[NOI2014]购票(斜率优化dp+CDQ分治+点分治)

2021-04-06 09:04:53  阅读:219  来源: 互联网

标签:qu dep ll 分治 NOI2014 int CDQ mx hea


题目:洛谷P2305LOJ#2249

题目描述:

给定一棵\(n\)个点的树,根为\(1\),树上每条边的长度已知,从每个点\(x\)到根的方法为:选择一个祖先\(a\),到达它需要消耗的代价为\(dis_{x,a} \cdot p_{x} + q_{x}\),然后依次往上,直到到达根,其中\(dis_{x,y}\)表示的是树上两点\(x,y\)之间的距离,\(p,q\)均为题目给定的参数,同时还给定了一个限制,就是点\(x\)往上跳祖先,不能直接跳到与他距离超过\(limit_{x}\)的祖先,请你求出每个点到达根的最小代价

\(n \leq 2 \cdot 10^{5}\),保证答案不超过\(longlong\)范围

蒟蒻题解:

记\(dep[x]\)为点\(x\)在树上的深度

很容易推出裸的\(dp\)方程:

\[f[i] = f[j] + p[i] \cdot (dep[i] - dep[j]) + q[i] \]

发现它有的项既有\(i\)又有\(j\),考虑斜率优化,将式子转换成:

\[f[j] = p[i] \cdot dep[j] + (f[i] - p[i] \cdot dep[i] - q[i]) \]

其中\(y = f[j], x = dep[j], k = p[i], b = f[i] - p[i] \cdot dep[i] - q[i]\)

那么这题如果不考虑限制\(l_{x}\)的话,就是斜率优化维护一个下凸包+二分+单调栈就可以了

那么加上限制\(limit_{x}\)呢?

如果在把凸包前面不满足限制的一段删掉,会出现下面这种情况:

如图,红点在斜率优化过程中被删除,蓝线是限制\(limit_{x}\),所以在限制\(limit_{x}\)的条件下,红点可能是合法的,不应该被删除

树上问题是由一条一条链拼起来的,先以链上问题思考,我们可以CDQ分治,把这条链分成两段,计算上方对下方的影响,至于拆成多段,我们可以考虑点分治

具体来讲,我们当前的根为\(x\),找出当前子树重心\(y\),计算\(x子树\)除去\(y\)下方所有节点的答案,然后再CDQ分治计算\(y\)及\(y\)上方的答案对\(y\)下方的影响

我们把\(y\)下方的所有点取出来,按\(dep_{i}-limit_{i}\)从大到小排序,然后对于\(x\)到\(y\)上的点,按深度从大到小加入凸包内,然后在凸包上二分计算答案

CDQ分治主要体现在思想,算法以点分治+二分为主,时间复杂度是\(\theta(n\ log^{2}n)\)

注意:

  • 斜率优化时,如果不计算斜率,而是叉乘的话会爆\(longlong\)
  • 点分治找重心时,如果有多个重心,且根为其中一个重心,应取根为重心,不然会死循环(例如根为\(x\),且仅有\(x->y\)一条边,重心为\(y\),每次操作都是删去重心的孩子,所以它会一直保留\(x\),又保留\(y\),就会导致死循环)

参考程序:

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;
typedef double dl;

const int N = 200005;
const dl eps = 1e-6;
const ll Inf = 2e18;
int n, rt, mn, cnt, h, t, res, a[N], qu[N], fa[N], p[N], siz[N], mx[N], hea[N], nxt[N], to[N];
ll len[N], q[N], lt[N], dep[N], f[N];
bool b[N];

inline ll read()
{
	char c = getchar();
	ll ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void write(ll x)
{
	int num = 0;
	char sc[25];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar('\n');
}

inline int max(int x, int y)
{
	return x > y ? x : y;
}

inline ll min(ll x, ll y)
{
	return x < y ? x : y;
}

inline void add(int x, int y)
{
	nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
}

inline void dfs(int x)
{
	for (Re i = hea[x]; i; i = nxt[i])
	{
		int u = to[i];
		dep[u] = dep[x] + len[u];
		dfs(u);
	}
}

inline void find_root(int x, int y)
{
	siz[x] = 1, mx[x] = 0;
	for (Re i = hea[x]; i; i = nxt[i])
	{
		int u = to[i];
		if (b[u]) continue;
		find_root(u, y);
		siz[x] += siz[u], mx[x] = max(mx[x], siz[u]);
	}
	mx[x] = max(mx[x], y - siz[x]);
	if (mx[x] <= mn) mn = mx[x], rt = x;//是<=,不是<
}

inline void dfs2(int x)
{
	a[++res] = x;
	for (Re i = hea[x]; i; i = nxt[i])
		if (!b[to[i]]) dfs2(to[i]);
}

inline bool cmp(int x, int y)
{
	return dep[x] - lt[x] > dep[y] - lt[y];
}

inline ll que(int x)
{
	int l = h + 1, r = t, s = h;
	while (l <= r)
	{
		int mid = l + r >> 1;
		if (((dl)f[qu[mid]] - f[qu[mid - 1]]) / ((dl)dep[qu[mid]] - dep[qu[mid - 1]] + eps) >= p[x]) s = mid, l = mid + 1;
		else r = mid - 1;
	}
	return f[qu[s]] - dep[qu[s]] * p[x] + dep[x] * p[x] + q[x];
}

inline void dfs1(int x, int y)
{
	if (y == 1) return;
	mn = n;
	find_root(x, y);
	int z = rt;
	for (Re i = hea[z]; i; i = nxt[i]) b[to[i]] = 1, y -= siz[to[i]];
	dfs1(x, y);
	res = 0;
	for (Re i = hea[z]; i; i = nxt[i]) dfs2(to[i]);
	sort(a + 1, a + res + 1, cmp);
	h = 0, t = -1;
	for (Re i = 1, j = z; i <= res; ++i)
	{
		while (j ^ fa[x] && dep[j] >= dep[a[i]] - lt[a[i]])
		{
			while (h < t && ((dl)f[qu[t]] - f[qu[t - 1]]) / ((dl)dep[qu[t]] - dep[qu[t - 1]] + eps) <= ((dl)f[j] - f[qu[t]]) / ((dl)dep[j] - dep[qu[t]] + eps)) --t;
			qu[++t] = j, j = fa[j];
		}
		if (h <= t) f[a[i]] = min(f[a[i]], que(a[i]));
	}
	for (Re i = hea[z]; i; i = nxt[i]) dfs1(to[i], siz[to[i]]);
}

int main()
{
	n = read(), mn = read();
	for (Re i = 2; i <= n; ++i)
	{
		fa[i] = read(), len[i] = read(), p[i] = read(), q[i] = read(), lt[i] = read(), f[i] = Inf;
		add(fa[i], i);
	}
	dfs(1), dfs1(1, n);
	for (Re i = 2; i <= n; ++i) write(f[i]);
	return 0;
}

标签:qu,dep,ll,分治,NOI2014,int,CDQ,mx,hea
来源: https://www.cnblogs.com/clfzs/p/14620154.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有