动态dp
引入
洪水:
小 A 走到一个山脚下,准备给自己造一个小屋。这时候,小 A 的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小 A 面前出现了一个瀑布。作为平民的小 A 只好老实巴交地爬山堵水。那么问题来了:
我们把这个瀑布看成是一个 \(n\) 个节点的有根树,编号为 \(1\) 到 \(n\) 的整数,\(1\) 号点为根。每个节点有权值(爬上去的代价)。小 A 要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小 A 的朋友觉得这样子太便宜小 A 了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。
小 A 觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。
朴素做法
\(f_u\) 为 \(u\) 子树的答案
\(f_u = \min(w_u, \sum_v f_v)\)。
这样会 \(TLE\)。
动态dp
我们对这棵树进行 重链剖分。
设 \(g_u\) 表示只考虑 \(u\) 的所有轻儿子的答案和。
\(f_u = \min(w_u, f_{son_u} + g_u)\)
定义广义矩阵乘法,\(C = A * B\),则
\[C_{i, j} = min_{k} (A_{i, k} + B_{k, j})
\]
我们要找到转移矩阵,满足
\(U* \begin{pmatrix}f_{son_{u}} \\ 0\end{pmatrix} = \begin{pmatrix}f_{u} \\ 0\end{pmatrix}\)
可以得到:
\[U =\begin{pmatrix}g_u & w_u\\ inf & 0 \end{pmatrix}
\]
每次只有在切换重链的时候会改变一个点的矩阵。
因为这个矩阵满足结合律,所以一条重链上的的 \(U\) 的乘积可以用线段树计算。
所以时间复杂度为 \(O(n \log n^2)\)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10, inf = 1e15 + 10;
struct Mat {
int a[2][2];
Mat() { a[0][0] = a[0][1] = a[1][1] = a[1][0] = inf; }
Mat operator*(const Mat& T) const {
Mat res;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
res.a[i][j] = min(res.a[i][j], a[i][k] + T.a[k][j]);
}
}
}
return res;
}
};
Mat g[N];
int n;
vector<int> edge[N];
int val[N];
int sze[N], f[N], dep[N], fa[N], son[N], top[N], End[N], dfn[N], in[N], tot;
void dfs1(int u, int f) {
sze[u] = 1;
dep[u] = dep[f] + 1;
fa[u] = f;
for (auto v : edge[u]) {
if (v == f)
continue;
dfs1(v, u);
sze[u] += sze[v];
if (sze[v] > sze[son[u]])
son[u] = v;
}
}
void dfs2(int u, int tp) {
dfn[u] = ++tot, in[tot] = u;
top[u] = tp, End[tp] = max(End[tp], tot);
g[u].a[0][0] = 0, g[u].a[0][1] = val[u];
g[u].a[1][0] = inf, g[u].a[1][1] = 0;
if (son[u]) {
dfs2(son[u], tp);
f[u] += f[son[u]];
} else
g[u].a[0][0] = inf;
for (auto v : edge[u]) {
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
f[u] += f[v];
g[u].a[0][0] += f[v];
}
if (son[u])
f[u] = min(f[u], val[u]);
else
f[u] = val[u];
// cout << u << " " << f[u] << "\n";
}
Mat t[N * 4];
void build(int p, int l, int r) {
if (l == r) {
t[p] = g[in[l]];
return;
}
int mid = (l + r) / 2;
build(p * 2, l, mid), build(p * 2 + 1, mid + 1, r);
t[p] = t[p * 2] * t[p * 2 + 1];
}
void change(int p, int L, int R, int x) {
if (L > x || R < x)
return;
if (L == R) {
t[p] = g[in[L]];
return;
}
int mid = (L + R) / 2;
change(p * 2, L, mid, x), change(p * 2 + 1, mid + 1, R, x);
t[p] = t[p * 2] * t[p * 2 + 1];
}
Mat query(int p, int L, int R, int l, int r) {
if (l <= L && R <= r) {
return t[p];
}
int mid = (L + R) / 2;
if (l <= mid && mid < r)
return query(p * 2, L, mid, l, r) * query(p * 2 + 1, mid + 1, R, l, r);
if (l <= mid)
return query(p * 2, L, mid, l, r);
return query(p * 2 + 1, mid + 1, R, l, r);
}
void upd(int x, int v) {
val[x] += v;
g[x].a[0][1] = val[x];
Mat bef, aft;
while (x) {
bef = query(1, 1, n, dfn[top[x]], End[top[x]]);
change(1, 1, n, dfn[x]);
aft = query(1, 1, n, dfn[top[x]], End[top[x]]);
x = fa[top[x]];
g[x].a[0][0] = g[x].a[0][0] - min(bef.a[0][0], bef.a[0][1]) + min(aft.a[0][0], aft.a[0][1]);
}
}
int qry(int u) {
Mat res = query(1, 1, n, dfn[u], End[u]);
return min(res.a[0][0], res.a[0][1]);
}
signed main() {
cin.tie(0), cout.tie(0);
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> val[i];
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
dfs1(1, 0), dfs2(1, 1);
for (int i = 1; i <= n; i++) {
End[i] = End[top[i]];
}
build(1, 1, n);
int T;
cin >> T;
while (T--) {
char op;
int x, y;
cin >> op >> x;
if (op == 'C') {
cin >> y;
upd(x, y);
} else
cout << qry(x) << "\n";
}
return 0;
}

浙公网安备 33010602011771号