【题解】洛谷 P11673 [USACO25JAN] Median Heap G [树形 dp]
P11673 [USACO25JAN] Median Heap G - 洛谷 (luogu.com.cn)
读完题,发现交换不需要代价,但改变需要,交换还是强制的。
我们定义 为
的中位数,定义如下函数:
发现:
也就是说,我们不必在意每个节点权值之间的大小关系,而只用关注节点权值和 m 的关系。
考虑构造树形 dp,f[x][0 / 1 / 2] 分别代表节点 x 小于 / 等于 / 大于 m 的代价。
转移时,枚举 x 与它的左右孩子的 0 / 1 / 2 状态。
假设枚举 x 的旧状态为 k,x 的新状态就是三个状态的中位数(因为交换是强制的)。
代价为 x 变成 k 的代价 + 左右孩子枚举的状态代价。
这样时间复杂度为 ,悬。
考虑先提前给询问数组按 m 从小到大排序,最后按顺序遍历。
定义一个指针,每次只更新小于等于 m 的节点。
时间复杂度 。
但这样还不够,只有一个指针时,如果当前 m 大于 上一个 m。
那等于上一个 m 的节点应该改变,但单指针只会继续往后增加。
考虑使用双指针,一个处理小于 m,一个处理大于 m,能完全覆盖到。
代码:
#include<bits/stdc++.h>
using namespace std;#define lp (p << 1)
#define rp ((p << 1) | 1)typedef long long LL;
const int N = 2e5 + 10;
const LL inf = 1e18;LL a[N], c[N];
int n;struct node {LL a; int id;
} p[N];struct qus {LL m; int id;
} q[N];bool cmpa(node na, node nb) {return na.a < nb.a;
}bool cmpb(qus qa, qus qb) {return qa.m < qb.m;
}LL f[N][3], ans[N];int get_med(int i, int j, int k) {return i + j + k - min({i, j, k}) - max({i, j, k});
}void update(int p, LL m) {f[p][0] = f[p][1] = f[p][2] = inf;int w[3];if (a[p] < m) {w[0] = 0;w[1] = w[2] = c[p];}if (a[p] == m) {w[1] = 0;w[0] = w[2] = c[p];}if (a[p] > m) {w[2] = 0;w[0] = w[1] = c[p];}if (rp > n) {f[p][0] = w[0];f[p][1] = w[1];f[p][2] = w[2];return ;}for (int i = 0; i <= 2; i ++) {for (int j = 0; j <= 2; j ++) {for (int k = 0; k <= 2; k ++) {int mid = get_med(i, j, k);f[p][mid] = min(f[p][mid], f[lp][i] + f[rp][j] + w[k]);}}}
}void pushup(int p, int m) {while (p) {update(p, m);p >>= 1;}
}int main () {ios::sync_with_stdio(false);cin.tie(0);cin >> n;for(int i = 1; i <= n; i ++) {cin >> a[i] >> c[i];p[i] = {a[i], i};}sort(p + 1, p + n + 1, cmpa);int Q; cin >> Q;for (int i = 1; i <= Q; i ++) {cin >> q[i].m;q[i].id = i;}sort(q + 1, q + Q + 1, cmpb);for (int i = 1; i <= n; i ++) {for (int j = 0; j <= 2; j ++) {f[i][j] = inf;}}for (int i = n; i >= 1; i --) {update(i, 0);}int la = 1, lb = 1;for (int i = 1; i <= Q; i ++) {while (la <= n && p[la].a < q[i].m) {pushup(p[la].id, q[i].m);la ++;}while (lb <= n && p[lb].a <= q[i].m) {pushup(p[lb].id, q[i].m);lb ++;}ans[q[i].id] = f[1][1];}for (int i = 1; i <= Q; i ++) {cout << ans[i] << "\n";}
}