「题解」 P2619 [国家集训队] Tree I
AtomAlpaca · · 题解
[国家集训队] Tree I
题意
给出一个无向带权连通图,每条边是黑色或白色。求一棵最小权的恰好有
详情请咨询
题解
凸性的证明思路和最小度限制生成树类似。首先对于每个白边
因此如果第
于是二分一个增量,每次将所有白边权值加上这个增量,再和黑边一起跑最小生成树,根据最小生成树中白边的数量调整增量即可。
2025/11/20 补:经 @do_while_true 老师提醒补充一下增量构造的正确性,也即我们总能通过删除一条黑边加入一条白边得到从
设图拟阵
设
则
同理有
由于
取等,故仅交换这一组边能达到最优,证毕
上高三好累,我想睡觉。
代码
#include <bits/stdc++.h>
using std::cin;
using std::cout;
const int MAX = 1e5 + 5;
int n, m, k, u, v, w, c, cnt, res, ans;
int f[MAX];
struct E { int u, v, w, c; } e[MAX];
int find(int x) { if (f[x] == x) { return x; } return f[x] = find(f[x]); }
bool cmp(E a, E b) { if (a.w == b.w) { return a.c < b.c; } return a.w < b.w; }
void check(int x)
{
res = cnt = 0;
for (int i = 1; i <= m; ++i) { if (e[i].c == 0) { e[i].w += x; } }
for (int i = 1; i <= n; ++i) { f[i] = i; }
std::sort(e + 1, e + m + 1, cmp);
for (int i = 1, tmp = 0; i <= m and tmp != n - 1; ++i)
{
int fu = find(e[i].u), fv = find(e[i].v);
if (fu == fv) { continue; }
f[fv] = fu; if (e[i].c == 0) { ++cnt; } res += e[i].w; ++tmp;
}
for (int i = 1; i <= m; ++i) { if (e[i].c == 0) { e[i].w -= x; } }
}
int main()
{
cin >> n >> m >> k;
for (int i = 1; i <= m; ++i)
{
cin >> u >> v >> w >> c;
e[i] = {u + 1, v + 1, w, c};
}
int l = -111, r = 111, mid = 0;
while (l <= r)
{
mid = (l + r) >> 1;
check(mid);
if (cnt < k) { r = mid - 1; } else { l = mid + 1; ans = res - k * mid; }
}
cout << ans;
return 0;
}