题目描述
小 A 走到一个山脚下,准备给自己造一个小屋。这时候,小 A 的朋友(op
,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小 A 面前出现了一个瀑布。作为平民的小 A 只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个 nn 个节点的树,每个节点有权值(爬上去的代价)。小 A 要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小 A 的朋友觉得这样子太便宜小 A 了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小 A 觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。
Solution
也是挺基础的一道ddp入门题。 状态比模板题还少一维。
设$dp_i$代表把$i$与它的子树内的叶节点断开所需的最少距离,有$dp_u=min(sum dp_v, a_u)$ 还是很套路地把它拆成和重儿子相关和不相关。
$g_u=sum_{v e son(u)} dp_v,dp_u=min(g_u+dp_{son(u)},a_u+0)$。
转移矩阵就很好构造了,代码和模板题大同小异。
Code
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int N = 200010; namespace IO{ template <typename T> void read(T &x) { T f = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0'; x *= f; } template <typename T> void write(T x) { if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void print(T x) { if (x < 0) x = -x, putchar('-'); write(x); putchar(' '); } } using namespace IO; int n, m; ll a[N]; namespace QXX{ struct node{ int pre, to; }edge[N << 1]; int head[N], tot; inline void add(int u, int v) { edge[++tot] = node{head[u], v}; head[u] = tot; } }using namespace QXX; namespace MATRIX{ template <typename T> void cmin(T &x, T y) {if (y < x) x = y;} template <typename T> void cmax(T &x, T y) {if (y > x) x = y;} struct Matrix{ ll arr[3][3]; }; inline void init(Matrix &X) { memset(X.arr, 0x3f, sizeof(X.arr)); } inline Matrix Mul(Matrix X, Matrix Y) { Matrix Z; init(Z); for (int i = 1; i <= 2; i++) for (int j = 1; j <= 2; j++) for (int k = 1; k <= 2; k++) cmin(Z.arr[i][j], X.arr[i][k] + Y.arr[k][j]); return Z; } inline void deburg(Matrix X) { for (int i = 1; i <= 2; i++) { for (int j = 1; j <= 2; j++) { cout << X.arr[i][j] << " "; } cout << endl; } cout << endl; } }using namespace MATRIX; namespace TCP{ Matrix A[N]; ll dp[N]; int dfn[N], pos[N], top[N], End[N], num; int sz[N], fa[N], son[N]; void dfs1(int x) { sz[x] = 1; for (int i = head[x]; i; i = edge[i].pre) { if (edge[i].to == fa[x]) continue; fa[edge[i].to] = x; dfs1(edge[i].to); sz[x] += sz[edge[i].to]; if (sz[edge[i].to] > sz[son[x]]) son[x] = edge[i].to; } } void dfs2(int x, int chain) { dfn[x] = ++num, top[x] = chain, pos[dfn[x]] = x; init(A[x]); A[x].arr[1][1] = 0, A[x].arr[1][2] = a[x]; A[x].arr[2][2] = 0; if (son[x]) dfs2(son[x], chain); else End[chain] = dfn[x], A[x].arr[1][1] = a[x], A[x].arr[2][1] = 0; for (int i = head[x]; i; i = edge[i].pre) { if (edge[i].to == fa[x] || edge[i].to == son[x]) continue; dfs2(edge[i].to, edge[i].to); A[x].arr[1][1] += dp[edge[i].to]; } if (son[x]) dp[x] = min(a[x], A[x].arr[1][1] + dp[son[x]]); else dp[x] = a[x]; } }using namespace TCP; namespace Segment_Tree{ struct Segment{ Matrix val; }tr[N << 2]; inline void push_up(int p) {tr[p].val = Mul(tr[p << 1].val, tr[p << 1 | 1].val);} void build(int p, int l, int r) { if (l == r) { tr[p].val = A[pos[l]]; return; } int mid = (l + r) >> 1; build(p << 1, l, mid); build(p << 1 | 1, mid + 1, r); push_up(p); } void change(int p, int l, int r, int Pos) { if (l == r) { tr[p].val = A[pos[l]]; return; } int mid = (l + r) >> 1; if (Pos <= mid) change(p << 1, l, mid, Pos); else change(p << 1 | 1, mid + 1, r, Pos); push_up(p); } Matrix query(int p, int l, int r, int L, int R) { if (L <= l && r <= R) { return tr[p].val; } int mid = (l + r) >> 1; if (R <= mid) return query(p << 1, l, mid, L, R); else if (L > mid) return query(p << 1 | 1, mid + 1, r, L, R); else return Mul(query(p << 1, l, mid, L, mid), query(p << 1 | 1, mid + 1, r, mid + 1, R)); } }using namespace Segment_Tree; inline void update(int x, ll val) {//将节点 a 的权值修改为 b A[x].arr[1][2] += (val - a[x]); if (!son[x]) A[x].arr[1][1] = A[x].arr[1][2]; a[x] = val; while (x) { Matrix bef = query(1, 1, n, dfn[top[x]], End[top[x]]); change(1, 1, n, dfn[x]); Matrix aft = query(1, 1, n, dfn[top[x]], End[top[x]]); x = fa[top[x]]; A[x].arr[1][1] += aft.arr[1][1] - bef.arr[1][1]; } } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); for (int i = 1, u, v; i < n; i++) read(u), read(v), add(u, v), add(v, u); read(m); dfs1(1); dfs2(1, 1); build(1, 1, n); while (m--) { char opt[2]; int x; scanf("%s", opt + 1); read(x); if (opt[1] == 'Q') { Matrix ans = query(1, 1, n, dfn[x], End[top[x]]); print(ans.arr[1][1]); } else { int val; read(val); update(x, a[x] + val); } } return 0; }