[BZOJ4712]洪水
Description
小 \(A\) 走到一个山脚下,准备给自己造一个小屋。这时候,小 \(A\) 的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小 \(A\) 面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个𝑛个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。
\(n\le 2\times 10^5, m\le 2\times 10^5\)
Solution
第一眼动态dp,还真是的,就当锻炼码力。
首先有转移
\[dp[u] = min\{\sum_{v\in son(u)}dp[v],\ a[u]\}
\]
记 \(son[u]\) 为u的重儿子(和上面的son(u)不是同一个东西),\(g[u]\) 为 u 虚儿子的 \(dp\) 值之和。
\[dp[u] = \min\{a[u], f[son[u]] + g[u]\}
\]
把这个东西放在重链上,假设我们维护了重链上每个点的 \(g[u]\) 和 \(dp[u]\) 那么查询x就查这条重链以x开头的一个类似于前缀的最小值。
修改的时候就暴力往上跳链修改点,会更新到一些点的 \(g[u]\),这些u就是两条链的交接处。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
typedef long long LL;
typedef unsigned long long uLL;
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define MP(x, y) std::make_pair(x, y)
#define DE(x) cerr << x << endl;
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO cerr << "GO" << endl;
using namespace std;
inline void proc_status()
{
ifstream t("/proc/self/status");
cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>()) << endl;
}
inline int read()
{
register int x = 0; register int f = 1; register char c;
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c xor 48), isdigit(c = getchar()));
return x * f;
}
template<class T> inline void write(T x)
{
static char stk[30]; static int top = 0;
if (x < 0) { x = -x, putchar('-'); }
while (stk[++top] = x % 10 xor 48, x /= 10, x);
while (putchar(stk[top--]), top);
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
const int maxN = 2e5 + 2;
#define ls (x << 1)
#define rs (x << 1 | 1)
#define Lson ls, l, mid
#define Rson rs, mid + 1, r
struct Node
{
LL g, f;
Node() { }
Node(LL g, LL f) : g(g), f(f) { }
Node operator + (const Node &B) const
{ return Node(g + B.g, min(f, g + B.f)); }
} t[maxN << 2], val[maxN];
void pushup(int x) { t[x] = t[ls] + t[rs]; }
void build(int x, int l, int r)
{
if (l == r) { t[x] = val[l]; return; }
int mid = l + r >> 1;
build(Lson); build(Rson);
pushup(x);
}
void modify(int x, int l, int r, int p)
{
if (l == r) { t[x] = val[l]; return; }
int mid = l + r >> 1;
if (p <= mid) modify(Lson, p);
else modify(Rson, p);
pushup(x);
}
Node query(int x, int l, int r, int L, int R)
{
if (L <= l and r <= R) { return t[x]; }
int mid = l + r >> 1;
if (R <= mid) return query(Lson, L, R);
if (mid < L) return query(Rson, L, R);
return query(Lson, L, mid) + query(Rson, mid + 1, R);
}
int n;
int ver[maxN << 1], nxt[maxN << 1], head[maxN], tot;
int fa[maxN], top[maxN], son[maxN], size[maxN], dfn[maxN], butt[maxN], dfst;
void link(int u, int v) { ver[++tot] = v, nxt[tot] = head[u], head[u] = tot; }
void dfs1(int u, int f)
{
fa[u] = f;
size[u] = 1;
for (int i = head[u]; i; i = nxt[i])
if (ver[i] != f)
{
dfs1(ver[i], u);
size[u] += size[ver[i]];
if (size[son[u]] < size[ver[i]])
son[u] = ver[i];
}
}
LL dp[maxN], a[maxN];
void dfs2(int u, int topf)
{
dp[u] = 0;
dfn[u] = ++dfst;
top[u] = topf;
if (son[u])
{
dfs2(son[u], topf);
dp[u] += dp[son[u]];
}
else butt[topf] = dfst, dp[u] = 1ll << 50;
for (int i = head[u]; i; i = nxt[i])
if (ver[i] != fa[u] and ver[i] != son[u])
{
dfs2(ver[i], ver[i]);
dp[u] += dp[ver[i]];
val[dfn[u]].g += dp[ver[i]];
}
chkmin(dp[u], a[u]);
}
void work(int u, LL v)
{
a[u] += v;
val[dfn[u]].f += v;
while (u)
{
LL tmp = query(1, 1, n, dfn[top[u]], butt[top[u]]).f;
modify(1, 1, n, dfn[u]);
LL y = query(1, 1, n, dfn[top[u]], butt[top[u]]).f;
u = fa[top[u]];
val[dfn[u]].g += y - tmp;
}
}
int main()
{
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read();
for (int i = 1; i < n; ++i)
{
int u = read(), v = read();
link(u, v);
link(v, u);
}
dfs1(1, 0), dfs2(1, 1);
for (int i = 1; i <= n; ++i)
val[dfn[i]].f = a[i];
build(1, 1, n);
int m = read();
while (m--)
{
char op[2];
int x;
LL y;
scanf("%s %d", op, &x);
if (op[0] == 'Q')
{
printf("%lld\n", query(1, 1, n, dfn[x], butt[top[x]]).f);
}
else
{
scanf("%lld", &y);
work(x, y);
}
}
return 0;
}