abc 267 e 题解
abc 267 e
题意
给定一个 \(N\) 个点 \(M\) 条边的简单无向图,第 \(i\) 条边连接 \(U_i, V_i\),点 \(i\) 的点权为 \(A_i\)。
重复以下操作 \(N\) 次:
- 选择一个未被移除的点 \(x\),移除 \(x\) 和与 \(x\) 相连的所有边。这次操作的代价为所有与 \(x\) 直接相连的点的点权之和。
请你求出 \(N\) 次操作代价的最大值的最小可能值。
思路
二分
我们可以发现,答案越小,越难达到,有单调性,可以想到 二分。
那么,我们应该如何判断每个值是否可行呢?
设我们当前要判断 \(X\) 是否可行,那么,我们肯定是先选择产生代价 \(\le x\) 的点删除。
而每次删除点,都只会使得与它直接相连的点所产生的代价变小,所以,每次只需要判断那些与它直接相连的点所产生的代价是否 \(\le X\) 即可。
令 \(V\) 为所有点权之和。
每次 Check()
的时间复杂度为 \(O(N + M)\),二分的时间复杂度为 \(O(\log V)\),所以总时间复杂度为 \(O((N + M) \times \log V)\)。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int n, m, a[N];
bool f[N];
vector<int> g[N];
ll s[N];
bool Check(ll x) {
queue<int> que;
int c = 0;
for (int i = 1; i <= n; i++) {
s[i] = f[i] = 0;
for (int j : g[i]) {
s[i] += a[j];
}
if (s[i] <= x) {
que.push(i), f[i] = 1;
}
}
while (!que.empty()) {
int u = que.front();
que.pop(), c++;
for (int v : g[u]) {
s[v] -= a[u];
if (s[v] <= x && !f[v]) {
que.push(v), f[v] = 1;
}
}
}
return c == n;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
while (m--) {
int u, v;
cin >> u >> v;
g[u].push_back(v), g[v].push_back(u);
}
ll l = 0, r = 2e14;
while (l < r) {
ll mid = (l + r) >> 1;
if (Check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l;
return 0;
}
贪心
每次选择产生代价最小的点删除,用堆或者 set
维护。
时间复杂度为 \(O((N + M) \times \log (N + M))\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, a[N];
long long sum[N], ans;
bool f[N];
vector<int> g[N];
struct Node {
long long s;
int id;
bool operator < (const Node &i) const {
return s > i.s;
}
};
priority_queue<Node> pq;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
while (m--) {
int u, v;
cin >> u >> v;
g[u].push_back(v), g[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
for (int j : g[i]) {
sum[i] += a[j];
}
pq.push({sum[i], i});
}
while (!pq.empty()) {
Node u = pq.top();
pq.pop();
if (!f[u.id]) {
f[u.id] = 1, ans = max(ans, u.s);
for (int v : g[u.id]) {
sum[v] -= a[u.id], pq.push({sum[v], v});
}
}
}
cout << ans;
return 0;
}