版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/88698592
【题目链接】
【思路要点】
- 建议参考 第一课堂陈江伦的《模拟费用流问题》课件。
- 我们称需要军队的地方为老鼠,军队为洞,那么我们可以花费一定代价移动老鼠和洞,使得所有老鼠均进洞,我们需要最小化总代价。
- 考虑使用贪心解决该问题,我们为每一只老鼠设定一个额外代价 ,其中 是一个足够小的数,表示将该老鼠和某一个洞匹配后额外的代价。由于我们会最小化总代价,因此这样将保证所有老鼠均进洞,我们只需把最后的答案加上 ,其中 为老鼠个数即可。
- 记节点 到根的距离为 ,树上 为 的点 之间的路径长度为 。考虑在 子树中的所有老鼠和洞,我们关心的仅仅是 的数值,而不关心它们具体在哪里。不难发现,上述额外代价也可以直接与 或 进行累加后看做老鼠和洞的固有属性,不妨记为 。
- 每当我们找到可以使当前总代价减小的匹配,即 的匹配,匹配之,答案将会变优。但这样找到的匹配很可能不是最终答案上的匹配,因此我们需要提供一个反悔的可能。考虑撤销本次匹配的代价,我们可以重新计算得到新的 。
- 至此,我们可以得出本题的算法,即从叶子结点出发,向上进行贪心。用两个小根堆来维护子树内 的集合,在合并两棵子树的同时合并它们对应的堆。当堆顶元素满足 时更新答案,并删除堆顶元素,加入 。
- 可以发现,若一对已经在 处匹配的老鼠和洞同时反悔,那么 的父边将会被老鼠正反经过两次,因此不反悔是更优的,从而不可能出现同时反悔的情况。那么,元素入堆的总次数将为 级别,总时间复杂度 。
- 可将堆中相同的元素并为一个,时间复杂度优化至 。
【代码】
#include<bits/stdc++.h> #include<ext/pb_ds/priority_queue.hpp> using namespace std; const int MAXN = 3e5 + 5; const long long INF = 1e12; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } ll ans, depth[MAXN]; vector <pair <int, int>> a[MAXN]; int n, m, cnthole[MAXN], cntmouse[MAXN]; __gnu_pbds :: priority_queue <ll, greater<ll>> hole[MAXN], mouse[MAXN]; void work(int pos, int fa, int len) { depth[pos] = depth[fa] + len; while (cnthole[pos]--) hole[pos].push(depth[pos]); while (cntmouse[pos]--) mouse[pos].push(depth[pos] - INF); for (auto x : a[pos]) if (x.first != fa) { work(x.first, pos, x.second); hole[pos].join(hole[x.first]); mouse[pos].join(mouse[x.first]); } while (!mouse[pos].empty() && !hole[pos].empty() && mouse[pos].top() + hole[pos].top() - 2 * depth[pos] < 0) { ll m = mouse[pos].top(), h = hole[pos].top(), s = m + h - 2 * depth[pos]; ans += s; mouse[pos].pop(), hole[pos].pop(); mouse[pos].push(m - s), hole[pos].push(h - s); } } int main() { read(n); for (int i = 1; i <= n - 1; i++) { int x, y, z; read(x), read(y), read(z); a[x].emplace_back(y, z); a[y].emplace_back(x, z); } for (int i = 1; i <= n; i++) { read(cnthole[i]), read(cntmouse[i]); int tmp = min(cnthole[i], cntmouse[i]); cnthole[i] -= tmp, m += cntmouse[i] -= tmp; } work(1, 0, 0); writeln(ans + INF * m); return 0; }