外观
LHn 朋友写信给他的题目的题解(LHn 不许看)
警告
LHn 不许看!
警告
LHn 不许看!
警告
LHn 不许看!
题意
有一棵以 1 为根的树,点有点权 wi 和目标 ai。给定数 c。
可以进行如下操作任意次:选择 u,v 满足 u 为 v 的父结点,交换 wu,wv 然后把 wu 加上 c,wv 减去 c。
最小化 i=1∑n∣wi−ai∣。
这道题需要转换视角。我们不再关注每一个点如何映射到点权,而是关注每一个点权如何映射到点。
注意到点权每次上移必定 +c,每次下移必定 −c。因此,将点权 w 从 u 移到 v,它一定变为 w+c(du−dv),其中 du 为 u 的深度。
不难证明树上交换一定可以交换到每个排列(每次到位一个叶子,然后变成子问题),于是答案相当于
p∈Pmini=1∑n∣wpi+cdpi−cdi−ai∣分离变量换元p∈Pmini=1∑n∣spi−ti∣
这是经典问题,对 si 和 ti 分别排序即可,调整可证。
Code(未验证)
#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;
}