题解 CF70E【Information Reform】

题解 CF70E【Information Reform】

题目描述

\(n\) 个点的树,边权为 \(1\)。可以花费常数 \(k\),在一个点上建基站。每个点 \(i\) 需要找到离他最近的基站 \(a_i\),花费 \(d[dis(i, a_i)]\)。一种方案的总花费是建基站的花费加上每个点的花费之和。最小化总花费。输出方案 \(a_i\)。数组 \(d\) 单调不降,\(d_0=0\)\(n\leq 180\)

solution

\(f(u, x)\) 表示考虑完了点 \(u\) 的子树中所有点的花费,点 \(u\) 找到的 \(a_u=x\),注意 \(x\) 这个基站的花费已经计算。转移枚举儿子 \(v\) 用哪个基站,用它自己的 \(a_v\) 还是 \(x\)

\[f(u, x)=d[dis(u, x)]+k+\sum_{v\in son(u)}\min(f(v, x)-k, f(v, a_v)) \]

\(f(v, x)-k\) 就是用基站 \(x\)\(-k\) 是为了不和外面的 \(+k\) 重复计算;\(f(v, a_v)\) 就是用 \(v\) 自己的基站。算完以后,\(a_u\) 就是 \(\arg\min_xf(u, x)\)

对于输出的方案,重新自顶向下 dfs 一遍。看一看是 \(f(v, a_u)-k\) 更优还是 \(f(v, a_v)\) 更优,如果前者更优,说明 \(v\) 要用基站 \(a_u\),将 \(a_v\) 篡改为 \(a_u\),然后向 \(v\) 搜索。

\(O(n^3)\)

code

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int n, k, d[210], dis[210][210], g[210], ans[210];
LL f[210][210];
void floyed(int k) {
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    }
  }
}
void dfs(int u, int fa = 0) {
  for (int v = 1; v <= n; v++)
    if (dis[u][v] == 1 && v != fa) {
      dfs(v, u);
      for (int x = 1; x <= n; x++) f[u][x] += min(f[v][x] - k, f[v][g[v]]);
    }
  for (int x = 1; x <= n; x++) f[u][x] += d[dis[u][x]] + k;
  g[u] = u;
  for (int x = 1; x <= n; x++)
    if (f[u][x] < f[u][g[u]]) g[u] = x;
}
void research(int u, int fa = 0) {
  int x = ans[u] = g[u];
  for (int v = 1; v <= n; v++)
    if (dis[u][v] == 1 && v != fa) {
      if (f[v][x] - k < f[v][g[v]]) g[v] = x;
      research(v, u);
    }
}
int main() {
  //    #ifdef LOCAL
  //            freopen("input.in","r",stdin);
  //    #endif
  memset(dis, 0x3f, sizeof dis);
  scanf("%d%d", &n, &k);
  for (int i = 1; i < n; i++) scanf("%d", &d[i]);
  for (int i = 1, u, v; i < n; i++)
    scanf("%d%d", &u, &v), dis[u][v] = dis[v][u] = 1;
  for (int i = 1; i <= n; i++) dis[i][i] = 0;
  for (int i = 1; i <= n; i++) floyed(i);
  dfs(1), printf("%lld\n", f[1][g[1]]), research(1);
  for (int i = 1; i <= n; i++) printf("%d%c", ans[i], " \n"[i == n]);
  return 0;
}
posted @ 2024-03-30 16:02  caijianhong  阅读(12)  评论(0编辑  收藏  举报