题目链接

 这个$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;
}
View Code

相关文章:

  • 2021-09-16
  • 2021-12-21
  • 2021-08-29
  • 2022-03-02
  • 2022-12-23
  • 2022-12-23
  • 2021-10-03
  • 2022-02-05
猜你喜欢
  • 2021-12-16
  • 2021-09-13
  • 2021-07-28
  • 2021-07-29
  • 2021-11-11
  • 2021-08-15
  • 2022-12-23
相关资源
相似解决方案