noip模拟赛 旅行
分析:一个贪心的想法是每次找到根的点权和最大的点进行操作,关键是怎么维护.每次找最大值,修改后会对这条链上每个点的子树上的点造成影响,可以用线段树来维护.找最大值就是区间求最大值嘛,对子树进行操作利用dfs序维护一下就好了.记录一下最大值的位置,每次从这个位置向上跳并对它的子树进行修改直到当前点的点权为0.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 200010; typedef long long ll; int n, k, a[maxn], fa[maxn],head[maxn], to[maxn], nextt[maxn],tot = 1, d[maxn], l[maxn], r[maxn], pos[maxn], dfs_clock; int maxx[maxn << 2], tag[maxn << 2], p[maxn << 2]; ll ans; void add(int x, int y) { to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void dfs(int u, int from, int dist) { d[u] = dist; l[u] = ++dfs_clock; pos[dfs_clock] = u; for (int i = head[u]; i; i = nextt[i]) { int v = to[i]; if (v != from) { dfs(v, u, dist + a[v]); fa[v] = u; } } r[u] = dfs_clock; } void pushup(int o) { if (maxx[o * 2] > maxx[o * 2 + 1]) { maxx[o] = maxx[o * 2]; p[o] = p[o * 2]; } else { maxx[o] = maxx[o * 2 + 1]; p[o] = p[o * 2 + 1]; } } void build(int o, int l, int r) { if (l == r) { maxx[o] = d[pos[l]]; p[o] = pos[l]; return; } int mid = (l + r) >> 1; build(o * 2, l, mid); build(o * 2 + 1, mid + 1, r); pushup(o); } void pushdown(int o) { if (tag[o] != 0) { tag[o * 2] += tag[o]; tag[o * 2 + 1] += tag[o]; maxx[o * 2] += tag[o]; maxx[o * 2 + 1] += tag[o]; tag[o] = 0; } } void update(int o, int l, int r, int x, int y,int v) { if (x <= l && r <= y) { maxx[o] += v; tag[o] += v; return; } pushdown(o); int mid = (l + r) >> 1; if (x <= mid) update(o * 2, l, mid, x, y, v); if (y > mid) update(o * 2 + 1, mid + 1, r, x, y, v); pushup(o); } void change(int x) { while (a[x]) { update(1, 1, n, l[x], r[x], -a[x]); a[x] = 0; x = fa[x]; } } int main() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); add(u, v); add(v, u); } dfs(1, 0, a[1]); build(1, 1, n); for (int i = 1; i <= k; i++) { ans += maxx[1]; change(p[1]); } printf("%lld\n", ans); return 0; }