这个$dark$题,嗯,不想说了。
法一:动态$dp$
虽然早有听闻动态$dp$,但到最近才学,如果你了解动态$dp$,那就能很轻松做出这道题了。故利用这题在这里科普一下动态$dp$的具体内容。
我们先不考虑点上的强制选不选的限制,这是一个最小权边覆盖问题,大家肯定都会这道题的$O(nm)$的做法,这是一个很经典的树形$dp$。具体来讲就是一下两个转移:
$$f_{x, 0} = \sum_{v} f_{v, 1} \qquad f_{x, 1} = a_{x} + \sum_{v} min(f_{v, 0} , f_{v, 1})$$
其中$f_{x, 0/1}$表示$x$这个点选/不选时$x$这个子树下的最少花费,$v$是$x$的亲儿子。
问题在树上,我们通常考虑树链剖分,并用$s(x)$表示$x$的重儿子。同时我们引出有关$x$新函数$g$如下:
$$g_{x, 0} = \sum_{v, v \neq s(x)} f_{v, 1} \qquad g_{x, 1} = a_{x} + \sum_{v, v \neq s(x)} min(f_{v, 0}, f_{v, 1})$$
于是有关$f$的转移可以改写成:
$$f_{x, 0} = f_{s(x), 1} + g_{x, 0} \qquad f_{x, 1} = min(f_{s(x), 0}, f_{s(x), 1}) + g_{x, 1}$$
这么做的目的在于把重儿子单独分离开来,这样在$g$中是不包含重儿子的信息的。我们过一会就能看到它的用处。
上述改写后的是一个有加法和取$min$的一个转移,我们把矩阵乘法中的乘法变成加法,把加法变成取$min$,那我们可以用一个线性变换来描述它,我们称它为$x$上的矩阵:
$$\begin{bmatrix}\infty & g_{x,0} \\g_{x,1} & g_{x, 1} \end{bmatrix}\begin{pmatrix} f_{s_{x},0} \\f_{s_x,1}\end{pmatrix}=\begin{pmatrix}f_{x,0} \\f_{x,1}\end{pmatrix}$$
特别的,我们有单位矩阵: $\begin{bmatrix}0 & \infty \\\infty & 0 \end{bmatrix}$。
这么做的好处在于原本一个自下而上的$dp$,可以被转变为矩阵乘法,一个点$x$的$f$可以由$x$点到它所在的重链的链尾上所有矩阵的乘积表示。我们可以用线段树维护链上矩阵的乘积,就能快速算得我们想要的$dp$值。
我们考虑如果要修改某一个点$x$的点权,我们如何维护矩阵的变化。首先我们都知道只有$x$的祖先的$dp$值可能会变化,并且如果$x$所在的儿子是某个祖先$y$的重儿子,那$g_y$就不会变化。由于我们的矩阵中只有关于$g$的信息,故$y$的矩阵也不会变化。所以事实上会发生变化的矩阵只有祖先链上的$O(logn)$条轻边的父亲的矩阵。我们可以自下而上每次暴力跳到那几条轻边,先在线段树上查得轻边儿子的$f$,然后把它父亲的$g$更新,修改矩阵。那么我们就能$O(log^2n)$维护点权修改了。注意这里我们每次会重新算链头的$f$值,所以任意时刻链头的$f$值都是对的,而非链头的点的$f$值是不一定准确的。
这就是动态$dp$的大致内容,我们可以整理一下思路。首先我们把$dp$的过程用线性变换替代,于是用矩阵的乘积表示某点的$dp$值。对于每次修改,我们暴力跳轻边来更新矩阵。
现在我们已经知道如何在支持修改点权的情况下,动态维护一棵子树下的最小权边覆盖问题。回过头来看这道题就显得非常容易了,题中的限制条件就可以通过把点权设成$-inf/inf$来实现。
这里我把矩阵乘法手动展开了,大概能快$400ms$左右。
#include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const int N = 100005; const LL INF = (LL)1e17; const LL BINF = INF / 10; int n, nq; int fa[N], tp[N], so[N], si[N], df[N], dw[N], li[N]; LL val[N], g[N][2], f[N][2]; struct Mat { LL v[2][2]; Mat(LL a = 0, LL b = 0) { v[0][0] = INF, v[0][1] = a; v[1][0] = v[1][1] = b; } friend Mat operator * (Mat &a, Mat &b) { static Mat c; c.v[0][0] = min(a.v[0][0] + b.v[0][0], a.v[0][1] + b.v[1][0]); c.v[0][1] = min(a.v[0][0] + b.v[0][1], a.v[0][1] + b.v[1][1]); c.v[1][0] = min(a.v[1][0] + b.v[0][0], a.v[1][1] + b.v[1][0]); c.v[1][1] = min(a.v[1][0] + b.v[0][1], a.v[1][1] + b.v[1][1]); return c; } } I; int yu, la[N], to[N << 1], pr[N << 1]; inline void Ade(int a, int b) { to[++yu] = b, pr[yu] = la[a], la[a] = yu; } void Dfs0(int x, int fat) { si[x] = 1, f[x][1] = val[x]; for (int i = la[x]; i; i = pr[i]) { if (to[i] == fat) continue; fa[to[i]] = x; Dfs0(to[i], x); si[x] += si[to[i]]; if (si[to[i]] > si[so[x]]) so[x] = to[i]; f[x][0] += f[to[i]][1]; f[x][1] += min(f[to[i]][0], f[to[i]][1]); } } void Dfs1(int x, int gr) { li[df[x] = ++*li] = x; tp[x] = gr, dw[x] = x, g[x][1] = val[x]; if (so[x]) Dfs1(so[x], gr), dw[x] = dw[so[x]]; for (int i = la[x]; i; i = pr[i]) if (to[i] != fa[x] && to[i] != so[x]) { Dfs1(to[i], to[i]); g[x][0] += f[to[i]][1]; g[x][1] += min(f[to[i]][0], f[to[i]][1]); } } namespace SE { int B; Mat t[N << 2 | 1]; void Bu(int n) { for (B = 1; B < n + 2; B <<= 1); for (int i = 1; i <= n; ++i) t[B + i] = Mat(g[li[i]][0], g[li[i]][1]); for (int i = B - 1; i; --i) t[i] = t[i << 1] * t[i << 1 | 1]; } void Mo(int x) { t[x + B] = Mat(g[li[x]][0], g[li[x]][1]); for ((x += B) >>= 1; x; x >>= 1) t[x] = t[x << 1] * t[x << 1 | 1]; } Mat Qr(int l, int r) { Mat r0 = I, r1 = I; for (l += B - 1, r += B + 1; l ^ r ^ 1; l >>= 1, r >>= 1) { if (~l & 1) r0 = r0 * t[l ^ 1]; if (r & 1) r1 = t[r ^ 1] * r1; } return r0 * r1; } } void Modify(int x, LL _v) { g[x][1] += _v - val[x], val[x] = _v; for (; x; x = fa[x]) { SE::Mo(df[x]), x = tp[x]; Mat tf = SE::Qr(df[x], df[dw[x]]); g[fa[x]][0] -= f[x][1]; g[fa[x]][1] -= min(f[x][0], f[x][1]); f[x][0] = tf.v[0][1], f[x][1] = tf.v[1][1]; g[fa[x]][0] += f[x][1]; g[fa[x]][1] += min(f[x][0], f[x][1]); } } int main() { I.v[0][1] = I.v[1][0] = INF; I.v[0][0] = I.v[1][1] = 0; scanf("%d%d%*s", &n, &nq); for (int i = 1; i <= n; ++i) scanf("%lld", &val[i]); for (int i = 1, x, y; i < n; ++i) { scanf("%d%d", &x, &y); Ade(x, y), Ade(y, x); } Dfs0(1, 0), Dfs1(1, 1); SE::Bu(n); for (int a, b, x, y; nq--; ) { scanf("%d%d%d%d", &x, &a, &y, &b); LL lx = val[x], ly = val[y]; Modify(x, a? lx - BINF : BINF); Modify(y, b? ly - BINF : BINF); LL ans = min(f[1][0], f[1][1]); ans += (a? BINF : 0) + (b? BINF : 0); printf("%lld\n", ans < BINF / 7? ans : -1); Modify(x, lx); Modify(y, ly); } return 0; }