Skip to content

查看源代码:
problem-in-a-letter.md

---
title: LHn 朋友写信给他的题目的题解(LHn 不许看)
createTime: 2025/12/8
categories:
    - IT
tags:
    - OI
---

::::caution
:::center
**LHn 不许看!**
:::
::::

::::caution
:::center
**LHn 不许看!**{style="font-size: large;"}
:::
::::

::::caution
:::center
**LHn 不许看!**{style="font-size: x-large;"}
:::
::::

:::problem 题意
有一棵以 $1$ 为根的树,点有点权 $w_i$ 和目标 $a_i$。给定数 $c$。

可以进行如下操作任意次:选择 $u,v$ 满足 $u$ 为 $v$ 的父结点,交换 $w_u, w_v$ 然后把 $w_u$ 加上 $c$,$w_v$ 减去 $c$。

最小化 $\displaystyle\sum_{i=1}^{n} |w_i - a_i|$。
:::

这道题需要转换视角。我们不再关注每一个点如何映射到点权,而是关注每一个点权如何映射到点。

注意到点权每次上移必定 $+c$,每次下移必定 $-c$。因此,将点权 $w$ 从 $u$ 移到 $v$,它一定变为 $w + c (d_u - d_v)$,其中 $d_u$ 为 $u$ 的深度。

不难证明树上交换一定可以交换到每个排列(每次到位一个叶子,然后变成子问题),于是答案相当于

$$
\min_{p \in \mathscr{P}} \sum_{i=1}^{n} |w_{p_i} + c d_{p_i} - c d_i - a_i| \xlongequal{\text{分离变量换元}} \min_{p \in \mathscr{P}} \sum_{i=1}^{n} |s_{p_i} - t_i|
$$

这是经典问题,对 $s_i$ 和 $t_i$ 分别排序即可,调整可证。

:::details Code(未验证)
```cpp
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=1e5+10;
ll w[maxn], a[maxn];
int fa[maxn];
vector<int> G[maxn];
ll dep[maxn], s[maxn], t[maxn];

void dfs(int u) {
    dep[u] = dep[fa[u]] + 1;
    for (int v: G[u]) {
        if (v == fa[u]) {
            continue;
        }
        dfs(v);
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    
    ll n, c; cin >> n >> c;
    for (int i=1; i<=n; i++) {
        cin >> w[i];
    }
    for (int i=1; i<=n; i++) {
        cin >> a[i];
    }
    for (int i=2; i<=n; i++) {
        cin >> fa[i];
        G[fa[i]].push_back(i);
    }
    
    dfs(1);
    
    for (int i=1; i<=n; i++) {
        s[i] = w[i] + c * dep[i];
        t[i] = a[i] + c * dep[i];
    }
    
    sort(s+1, s+n+1);
    sort(t+1, t+n+1);
    
    ll ans = 0;
    for (int i=1; i<=n; i++) {
        ans += abs(s[i] - t[i]);
    }
    cout << ans;
    
    return 0;
}
```
:::