国家集训队是不一样
题意:
- 很清晰不赘述
思路:
- 没写过应该想不到。
- 可以证明给所有白边枚举加权,一定有一种权值情况可以满足恰好 need 条白边,同时是最小生成树。
- 观察可以发现具有二分性,二分的不是答案,是给白边加的权值。
- 边界要注意,小情况要特判。
C o d e : Code: Code:
#include<bits/stdc++.h>
#include<unordered_set>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define ul (u << 1)
#define ur (u << 1 | 1)
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
const int N = 50010, M = 200010, MM = N;
int INF = 0x3f3f3f3f, mod = 998244353;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
struct edge
{
int a, b, w, ty;
bool operator<(const edge& ee)const {
if (w != ee.w)return w < ee.w;//升序
return ty < ee.ty;//白边优先
}
}ed[M];
int p[N], ans, sum, bian, cnt;
int find(int x) {
if (p[x] != x)p[x] = find(p[x]);
return p[x];
}
void kruskal() {
sort(ed, ed + m);
for (int i = 0; cnt != n - 1; i++) {
int a = ed[i].a, b = ed[i].b;
a = find(a), b = find(b);
if (a ^ b) {
cnt++;
if (ed[i].ty == 0)bian++;
sum += ed[i].w;
p[a] = b;
}
}
}
int main() {
cinios;
cin >> n >> m >> k;
forr(i, 0, m - 1) {
int a, b, x, t;
cin >> a >> b >> x >> t;
a++, b++;
ed[i] = { a,b,x,t };
}
ans = INF;
int l = -110, r = 110;
while (l < r)
{
int mid = l + r >> 1;
forr(i, 1, n)p[i] = i;
forr(i, 0, m - 1)if (ed[i].ty == 0)ed[i].w += mid;//给白边加权
sum = bian = cnt = 0;
kruskal();
//为什么独特判断?因为有可能没有 bian == k 的情况
//可以证明,此时 没选的边中有一条白边权值 和 选的边中的一条黑边相同
//替换就行,所以每一种 >= k 都考虑,ans 也不能取 min
//只有最后一次更新到的 ans 才是可以替换的
if (bian >= k) {
l = mid + 1;
ans = sum - k * mid;
}
else r = mid - 1;
forr(i, 0, m - 1)if (ed[i].ty == 0)ed[i].w -= mid;
}
cout << ans;
return 0;
}
/*
*/