ARC098D Donation

捐钱太亏了,我们倒序考虑,于是就变成了抢钱。

于是对于一个点的限制 \((a_u,b_u)\),令 \(c_u=\max\{0,a_u-b_u\}\),表示到这个点之前你至少拥有的钱数,并且到了这个点之后会抢到 \(b_u\) 的钱。

考虑建立 \(\text{Kruskal}\) 重构树使得树上每条边 \(fa_u\to u\)\(c_{fa_u}\ge c_u\),贪心地从某个叶子开始走,最优移动就一定在这棵树上。

这棵重构树可以用赋值的方法搞出来,给原图每条无向边 \((u,v)\) 赋一个权值 \(\operatorname{max}(c_u,c_v)\) 即可。这等价于按照 \(c_u\) 从小到大枚举点 \(u\),并找一个最浅的 \(v\) 使得 \(c_v\le c_u\) 并连边。

建出树后就可以 \(\text{dp}\) 了,令 \(f_u\) 为从 \(u\) 子树内抢到 \(u\) 所需的最小初始钱数。

  • 如果 \(u\) 是叶子,\(f_u=c_u\)
  • 否则 \(u\) 能从 \(v\in \operatorname{son}(u)\) 走过来,前提是至少有 \(f_v\) 的钱,并且走到 \(u\) 前至少有 \(c_u\) 的钱。由于是从 \(v\) 走过来的,所以会抢到 \(\sum\limits_{k\in \operatorname{subtree}(v)}b_k\) 的钱。所以 \(f_u=\min\limits_{v\in \operatorname{son}(u)}\{\max\{f_v,c_u-\sum\limits_{k\in \operatorname{subtree}(v)}b_k\}\}\)

处理出 \(s_u=\sum\limits_{k\in \operatorname{subtree}(u)}b_k\) 即可。答案为 \(f_{rt}+s_{rt}\)\(rt\)\(c\) 的值最大的点(即整棵树的根)。

#include <bits/stdc++.h>
#define int long long
namespace mystd {
	inline int read() {
	    char c = getchar();
	    int x = 0, f = 1;
	    while (c < '0' || c > '9') f = (c == '-') ? -1 : f, c = getchar();
	    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
	    return x * f;
	}
	inline void write(int x) {
	    if (x < 0) x = ~(x - 1), putchar('-');
	    if (x > 9) write(x / 10);
	    putchar(x % 10 + '0');
	}
}
using namespace std;
using namespace mystd;

const int maxn = 1e5 + 100;
struct edge { int u, v, cl; } e[maxn];
int n, m, a[maxn], b[maxn], c[maxn], f[maxn], s[maxn], fa[maxn], id[maxn], vis[maxn];
vector<int> g[maxn], gr[maxn];

int getf(int x) { return x == fa[x] ? x : fa[x] = getf(fa[x]); }
void dfs(int u) {
	s[u] = b[u], f[u] = 1e18;
	if (!g[u].size()) return f[u] = c[u], void();
	for (int v : g[u]) {
		dfs(v);
		f[u] = min(f[u], max(f[v], c[u] - s[v]));
		s[u] += s[v];
	}
}

signed main() {
	n = read(), m = read();
	for (int i = 1; i <= n; i++) a[i] = read(), b[i] = read(), c[i] = max(0ll, a[i] - b[i]), id[i] = i;
	for (int i = 1, u, v; i <= m; i++) gr[u = read()].push_back(v = read()), gr[v].push_back(u);
	sort(id + 1, id + n + 1, [](const int &x, const int &y) { return c[x] < c[y]; });
	for (int i = 1; i <= n; i++) fa[i] = i;
	for (int i = 1; i <= n; i++) {
		int u = id[i];
		for (int v : gr[u]) {
			if (!vis[v]) continue;
			int ru = getf(u), rv = getf(v);
			if (ru != rv) 
				fa[rv] = ru, g[u].push_back(rv);
		}
		vis[u] = 1;
	}
	int rt = id[n];
	dfs(rt);
	write(f[rt] + s[rt]);
	return 0;
}

posted @ 2023-07-20 17:26  Ender_32k  阅读(5)  评论(0编辑  收藏  举报