题解:P3651 展翅翱翔之时 (はばたきのとき)
weilycoder · · 题解
题目显然等价于将一个(内向)基环树森林修改为一个环。
思考一会发现图在变成环前可以看成一堆链。
则问题变成断掉总代价最小的边,使得图变为一些链。
发现如果原图是内向树的话做法显然,只需要对每个点保留边权最大的入边,将其余边断掉。
由于基环树可以通过破坏环上的一条边变成树,因此只需要枚举破坏的是哪条边即可。
这样暴力做的复杂度是
不妨先对每个点只保留边权最大的边,计算代价。
发现断掉的边(设其为
如果是第一种情况,则断掉这条边后,
如果是第二种情况,显然这条边已经被断掉了,代价不变。
注意需要特判原图已经只有一个环的情况。
至此,可以在
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
size_t find_circle(size_t curr, const vector<size_t> &nxt, vector<bool> &visited) {
while (!visited[curr])
visited[curr] = true, curr = nxt[curr];
return curr;
}
vector<size_t> get_circle(size_t start, const vector<size_t> &nxt) {
vector<size_t> circle;
size_t curr = start;
do
circle.push_back(curr), curr = nxt[curr];
while (curr != start);
return circle;
}
void mark_block(size_t curr, const vector<vector<size_t>> &prv, vector<bool> &visited) {
visited[curr] = true;
for (size_t p : prv[curr])
if (!visited[p])
mark_block(p, prv, visited);
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin.exceptions(cin.failbit | cin.badbit);
size_t n;
cin >> n;
vector<size_t> nxt(n);
vector<vector<size_t>> prv(n);
vector<int64_t> cost(n), prv_cost_sum(n);
vector<pair<int64_t, int64_t>> prv_cost_max(n);
auto update_maxvalue = [](pair<int64_t, int64_t> &cur, int64_t val) {
if (val > cur.first)
cur.second = cur.first, cur.first = val;
else if (val > cur.second)
cur.second = val;
};
for (size_t i = 0; i < n; ++i)
cin >> nxt[i] >> cost[i], --nxt[i];
for (size_t i = 0; i < n; ++i)
prv[nxt[i]].push_back(i);
for (size_t i = 0; i < n; ++i)
for (size_t p : prv[i])
prv_cost_sum[i] += cost[p], update_maxvalue(prv_cost_max[i], cost[p]);
int64_t total_cost = 0;
for (size_t i = 0; i < n; ++i)
total_cost += cost[i] - prv_cost_max[i].first;
vector<bool> visited(n, false), in_block(n, false);
for (size_t i = 0; i < n; ++i) {
if (visited[i])
continue;
size_t circle_start = find_circle(i, nxt, in_block);
mark_block(circle_start, prv, visited);
auto circle = get_circle(circle_start, nxt);
if (circle.size() == n)
break;
int64_t best_cut = numeric_limits<int64_t>::max();
for (size_t u : circle)
if (cost[u] == prv_cost_max[nxt[u]].first)
best_cut = min(best_cut, prv_cost_max[nxt[u]].first - prv_cost_max[nxt[u]].second);
else
best_cut = 0;
total_cost += best_cut;
}
cout << total_cost << '\n';
return 0;
}
:::