「UVA 12161」Ironman Race in Treeland
Posted -wallace-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「UVA 12161」Ironman Race in Treeland相关的知识,希望对你有一定的参考价值。
Description
给定一个 (n) 个结点的树,每条边有两个属性:长度 (L) 和花费 (D)。
现给定最大花费值 (m),求出花费总和不超过 (m) 的路径中长度的最大值。
Hint
(1le nle 3 imes 10^4, 1le mle 10^8, 1le L,Dle 10^3)
Solution
这种树上路径的问题,不难想到 点分治。
难点在于如何得到经过根的最优的路径。
我们一个个子结点来看,假如现在处理到子结点为 (x_k),如下图:
那么 (x_1, x_2,cdots,x_{k-1}) 的子树已经处理完了。
现在枚举子树 (x_k) 中的所有一个端点为根的路径,当枚举到一条路径的信息为 ((D^prime, L^prime)),我们就在子树 (x_1,x_2,cdots, x_{k-1}) 中的所有路径信息中匹配——找到其中 (D) 值 (le m - D^prime) 的所有路径中 (L) 值的最大值。
这样的话,处理方式就非常多了,比较好的做法可以平衡树,但这里介绍一种虽然效率稍劣但十分简单好理解且细节极少的 动态开点线段树 做法。
回到原来那个匹配的问题:我们把前 (k-1) 个子树的路径信息存入一个动态开点线段树中,对 (D) 值域(位置)开线段树,结点维护 (L) 的最大值。那么就可以求出位置 ([0,D^prime -1]) 的最大值,然后子树 (x_k) 处理完后,在把其中的路径信息以 (D) 为位置,(L) 为值,做 单点最值操作,即 ( ext{val}_D leftarrow max( ext{val}_D, L))。还有就是记得及时清空。
这样的算法复杂度是多少呢?动态开点线段树的空间一次修改是最大多开 (log U)((U) 为值域,下同)个结点,所以空间复杂度为 (O(nlog U))
由于深度也是最大 (log U) 层,所以时间复杂度为 (O(nlog n log U))。
Code
跑了 380ms
,效率还是挺可观的。
#include <algorithm>
#include <cstdio>
#include <utility>
#include <vector>
using namespace std;
const int N = 3e4 + 5;
namespace dysgt {
const int LogU = 27;
const int S = N * LogU;
const int Lim = 1e8;
int lc[S], rc[S];
int maxv[S];
int total = 0;
int root = 0;
inline void destroy(int x) {
lc[x] = rc[x] = maxv[x] = 0;
}
inline void modify(int p, int v) {
int l = 0, r = Lim;
if (!root) root = ++total;
for (register int x = root; l != r; ) {
maxv[x] = max(maxv[x], v);
int mid = (l + r) >> 1;
if (p <= mid) x = !lc[x] ? (lc[x] = ++total) : lc[x], r = mid;
else x = !rc[x] ? (rc[x] = ++total) : rc[x], l = mid + 1;
}
}
int getMax(int L, int R, int l = 0, int r = Lim, int x = root) {
if (!x) return 0;
if (L <= l && r <= R) return maxv[x];
int ret = 0, mid = (l + r) >> 1;
if (l <= mid) ret = max(ret, getMax(L, R, l, mid, lc[x]));
if (r > mid) ret = max(ret, getMax(L, R, mid + 1, r, rc[x]));
return ret;
}
void _S_clr(int x) {
if (!x) return;
_S_clr(lc[x]), _S_clr(rc[x]);
destroy(x);
}
inline void clear() {
_S_clr(root);
root = total = 0;
}
}
int n, m;
struct edge { int to, len, dam; };
vector<edge> G[N];
bool centr[N];
int maxp[N], size[N];
int root;
int getSize(int x, int f) {
size[x] = 1;
for (auto y : G[x])
if (y.to != f && !centr[y.to])
size[x] += getSize(y.to, x);
return size[x];
}
void getCentr(int x, int f, int t) {
maxp[x] = 0;
for (auto y : G[x]) {
if (y.to == f || centr[y.to]) continue;
getCentr(y.to, x, t);
maxp[x] = max(maxp[x], size[y.to]);
}
maxp[x] = max(maxp[x], t - size[x]);
if (maxp[x] < maxp[root]) root = x;
}
pair<int, int> path[N];
int tot;
void getPaths(int x, int f, int l, int d) {
if (d > m) return;
path[++tot] = {d, l};
for (auto y : G[x])
if (y.to != f && !centr[y.to])
getPaths(y.to, x, l + y.len, d + y.dam);
}
int ans;
void solve(int x) {
getSize(x, 0);
maxp[root = 0] = N;
getCentr(x, 0, size[x]);
int s = root;
centr[s] = true;
for (auto y : G[s])
if (!centr[y.to])
solve(y.to);
dysgt::clear(), dysgt::modify(0, 0);
for (auto y : G[s]) {
if (centr[y.to]) continue;
tot = 0, getPaths(y.to, x, y.len, y.dam);
for (register int i = 1; i <= tot; i++)
ans = max(ans, dysgt::getMax(0, m - path[i].first) + path[i].second);
for (register int i = 1; i <= tot; i++)
dysgt::modify(path[i].first, path[i].second);
}
centr[s] = false;
}
signed main() {
int T; scanf("%d", &T);
for (register int tc = 1; tc <= T; tc++) {
scanf("%d%d", &n, &m);
for (register int i = 1; i <= n; i++) {
G[i].clear();
maxp[i] = size[i] = 0;
centr[i] = false;
}
for (register int i = 1; i < n; i++) {
int u, v, l, d;
scanf("%d%d%d%d", &u, &v, &d, &l);
G[u].push_back({v, l, d});
G[v].push_back({u, l, d});
}
ans = 0, solve(1);
printf("Case %d: %d
", tc, ans);
}
return 0;
}
以上是关于「UVA 12161」Ironman Race in Treeland的主要内容,如果未能解决你的问题,请参考以下文章