CF1882D

题解 · 2023-09-26

传送门

CF1882D

题意

给出一颗树,节点编号为 $1 ... n$,每个点有一个权值 $a_i$。

你可以执行任意次操作:选择一个 $x$ 满足 $1 \le i \le n$,和一个正整数 $c$。并将 $x$ 子树中所有点的权值异或上 $c$,这次操作的代价为 $u \cdot c$,其中 $u$ 为子树 $x$ 的节点个数。

现在需要你求出,对于每个 $r,1 \le r \le n$,以 $r$ 为根时,让所有 $a_i$ 相等的最小代价。

$1 \le n \le 2 \cdot 10 ^ 5,1 \le a_i \le 2 ^ {20}$

题解

我们首先考虑,只需要求出以 $1$ 为根时的答案。

看到异或操作,而且 $a \le 2 ^ {20}$,可以想到对每一位分别处理。

接下来我们考虑树形DP,设 $f_{x,j,k}$ 表示将子树 $x$ 所有点权值的第 $j$ 位均变成 $k$ 的方案数,那么答案就是 $\sum_{j=0}^{20} {\min \{f_{1,j,0},f_{1,j,1}\}}$。

对于一个叶子节点,可以很容易地求出它的 $f$ 值。

对于任意非叶子节点 $x$。

若 $x$ 的第 $j$ 位为 $1$

  • 若要子树内所有点第 $j$ 位均为 $1$。我们必须在 $x$ 上进行偶数次操作,而偶数次异或不仅不会改变权值,而且会增加代价,所以我们不在 $x$ 上操作。有 $f_{x,j,1} = \sum_{y \in Son(x)}f_{y,j,1}$
  • 若要子树内所有点第 $j$ 位均为 $0$。我们必须对 $x$ 上进行奇数次操作,而奇数次操作与 $1$ 次操作的影响一样,但代价更多,所以我们只在 $x$ 上进行一次操作。有 $f_{x,j,0} = \sum_{y \in Son(x)}f_{y,j,0} + 2 ^ {j - 1} \cdot size_x$

对于 $x$ 的第 $j$ 位为 $0$ 时类似。

于是我们可以写出这个简化版问题的部分代码。

const int maxn = 200010;
int t, n, a[maxn];
int f[maxn][22][2], s[maxn][22][2], siz[maxn];
vector <int> e[maxn];
void dfs (int x, int fa) {
  siz = 1;
  for (int y : e) {
    if (y != fa){
      dfs (y, x);
      siz += siz[y];
      for (int j = 0; j < 20; j++) {
        s[j][0] += f[y][j][0];
        s[j][1] += f[y][j][1];
      }
    }
  }
  for (int j = 0; j < 20; j++) {
    if ((a >> j) & 1) {
      f[j][1] = s[j][1];
      f[j][0] = f[j][1] + siz * (1 << j);
    } else {
      f[j][0] = s[j][0];
      f[j][1] = f[j][0] + siz * (1 << j);
    }
  }
}

考虑完整问题。
我们需要求出每个点作为根的情况。可以做换根dp。设 $g_{x,j,k}$ 表示以 $x$ 为根,第 $j$ 位均为 $k$ 的最小代价。

要求出点 $x$ 作为根时的答案,我们可以从以 $fa_x$ 为根的答案转移过来。(这里的父子关系指的是在以 $1$ 为根时)。

考虑如何将 $g_{fa_x,j,0/1}$ 转移到 $g_{x,j,0/1}$,进行分讨论。

若 $x$ 的第 $j$ 位为 $1$

若 $fa_x$ 的第 $j$ 位为 $1$,根据上面计算的方法,我们直接算出 $g_{fa_x,j,1}$ 去掉子树 $x$ 时的答案,再加上 $x$ 所有儿子的代价。

  • $g_{x,j,1}=g_{fa,j,1}-f_{x,j,1}+\sum_{y \in Son(x)}{f_{y,j,1}}=g_{fa,j,1}-f_{x,j,1}+f_{x,j,1}=g_{fa,j,1}$。
    $g_{x,j,0}=g_{x,j,1}+2^{j-1}\cdot n$。

若$fa_x$的第$j$位为 $0$,我们需要先把$fa_x$作为根时除子树$x$的部分第$j$位都变成$1$,把$x$的所有儿子变成第$j$位变成$1$。

  • $g_{x,j,1}=g_{fa_x,j,1}-f_{x,j,0}-siz_x*2^{j-1}+\sum_{y \in Son(x)}{f_{y,j,1}}$。

$g_{x,j,0}$相当于在$g_{x,j,1}$的基础上多在$x$上做了个操作。

  • $g_{x,j,0}=g_{x,j,1}+2^{j-1}\cdot n$。

对于$x$的第$j$位为$0$也同理

完整代码

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define int long long
const int maxn = 200010;
int t, n, a[maxn];
int f[maxn][22][2], res[maxn], g[maxn][22][2], siz[maxn], s[maxn][22][2];
vector <int> e[maxn];
void dfs (int x, int fa) {
  siz = 1;
  for (int y : e) {
    if (y != fa){
      dfs (y, x);
      siz += siz[y];
      for (int j = 0; j < 20; j++) {
        s[j][0] += f[y][j][0];
        s[j][1] += f[y][j][1];
      }
    }
  }
  for (int j = 0; j < 20; j++) {
    if ((a >> j) & 1) {
      f[j][1] = s[j][1];
      f[j][0] = f[j][1] + siz * (1 << j);
    } else {
      f[j][0] = s[j][0];
      f[j][1] = f[j][0] + siz * (1 << j);
    }
  }
}
void dp (int x, int fa) {
  if (fa) {
    for (int j = 0; j < 20; j++) {
      if ((a >> j) & 1) {
        if ((a[fa] >> j) & 1) {
          g[j][1] = g[fa][j][1];
          g[j][0] = g[j][1] + n * (1 << j);
        } else {
          g[j][1] = g[fa][j][1] - f[j][0] - siz * (1 << j) + s[j][1];
          g[j][0] = g[j][1] + n * (1 << j);
        }
      } else {
        if ((a[fa] >> j) & 1) {
          g[j][0] = g[fa][j][0] - f[j][1] - siz * (1 << j) + s[j][0];
          g[j][1] = g[j][0] + n * (1 << j);
        } else {
          g[j][0] = g[fa][j][0];
          g[j][1] = g[j][0] + n * (1 << j);
        }
      }
      res += min (g[j][0], g[j][1]);
    }
  }
  for (int y : e) {
    if (y != fa) dp (y, x);
  }
}
signed main () {
  cin.tie (0) -> sync_with_stdio (false);
  cin >> t;
  while (t--) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
      for (int j = 0; j < 20; j++) {
        f[i][j][0] = f[i][j][1] = 0;
        g[i][j][0] = g[i][j][1] = 0;
        s[i][j][0] = s[i][j][1] = 0;
      }
      res[i] = siz[i] = 0;
      cin >> a[i];
      e[i].clear ();
    }
    for (int i = 1; i < n; i++) {
      int x, y; cin >> x >> y;
      e.push_back (y);
      e[y].push_back (x);
    }
    dfs (1, 0);
    for (int i = 0; i < 20; i++) {
      res[1] += min (f[1][i][0], f[1][i][1]);
      g[1][i][0] = f[1][i][0];
      g[1][i][1] = f[1][i][1];
    }
    dp (1, 0);
    for (int i = 1; i <= n; i++) cout << res[i] << ' ';
    cout << '\n';
  }
  return 0;
}
题解
Theme Jasmine by Kent Liao