P3620 [APIO/CTSC2007] 数据备份
这题和 P1792 [国家集训队 种树] 差不多,可以看看我的博客 https://www.cnblogs.com/maburb/p/18664870
题意:有n个点,你要用k根线两两连接,线的长度就是他们的距离,问最小总长度是多少。
一个不可能越过中间一个点去和其他点连接,因为很明显这样距离更长,举个例子,对于a, b, c, d这四个位置来说,a连b+c连d肯定小于a连d加b连c。所以我们只能选相邻的点连接,那么我们把所以两个点直接的连线都拿出来,总共n - 1条线,题意转换为从这些线里选k条,并且任意两条不能相邻。
那么我们贪心的想,优先选最小的,但是这样可能选不够k条,那么我们只能放弃一条线选他两边的两条线来增加线的数量,那么这个贡献是怎么变化的?假设每条线长度为\(a_i\), 我们一开始选了i, 贡献加\(a_i\),然后不选第i条选i-1和i+1,那么贡献是减去i的长度加上两边的长度,就是\(a_{i-1}\)+\(a_{i+1}\)+\(a_i\),我们在选择第i条线后用这个值替换它成为一个新的点,那么以后选到这个点就相当于进行了这个操作。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<i64> val(n + 1);
std::vector<int> l(n + 1), r(n + 1);
using PII = std::pair<i64, int>;
std::priority_queue<PII, std::vector<PII>, std::greater<PII> > heap;
for (int i = 1; i < n; ++ i) {
l[i] = i - 1;
r[i] = i + 1;
val[i] = a[i] - a[i - 1];
heap.push({val[i], i});
}
val[0] = val[n] = 1e9;
heap.push({val[0], 0});
heap.push({val[n], n});
auto del = [&](int p) -> void {
if (p <= 0 || p >= n) {
return;
}
l[r[p]] = l[p];
r[l[p]] = r[p];
};
i64 ans = 0;
std::vector<int> vis(n + 1);
while (k -- ) {
while (vis[heap.top().second]) {
heap.pop();
}
auto [v, id] = heap.top(); heap.pop();
ans += v;
// std::cout << id << " " << v << "\n";
val[id] = val[l[id]] + val[r[id]] - v;
heap.push({val[id], id});
vis[l[id]] = vis[r[id]] = 1;
del(l[id]); del(r[id]);
}
std::cout << ans << "\n";
}

浙公网安备 33010602011771号