Problem G: LZL的柠檬树
Time Limit: 1 Sec Memory Limit: 128 MB
Description
LZL的柠檬树长出的柠檬可酸了!如果你也想要这棵酸酸的柠檬树的话,那我们就一起来种吧!好心的琳姐共享了她的种植方案~
现在给你一个无向带权的连通图,每条边是黑色或白色的。
你只要求出一颗最小生成树,且这棵树的权值最小,而且刚好有K条白色边即可。
这棵树就是琳姐的柠檬树啦!
Input
第一行V,E,K分别表示点数,边数和需要的白色边数。
接下来E行,每行S,T,W,C表示这条边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
Output
一行表示所求柠檬树的边权和。
1<=V<=50000,1<=E<=100000,所有数据边权为[1,100]中的正整数。
题目保证有解
Sample Input
2 2 1
0 1 1 1
0 1 2 0
Sample Output
2
HINT
题解:(点我查看原题解
很经典的一道题目。考察二分+最小生成树。
我们考虑将所有的白色的边加上一个cost,如x至y有一条白色边权值为len,那我们将它的权值变为cost+len,然后我们做最小生成树,很明显,如果加上的cost越大,所选的白色边越少,所以我们就二分这个cost,直至刚好选了need条时,则答案就是生成树的权值-need*cost。
代码:
#include <bits/stdc++.h>
using namespace std;
int l = -105, r = 105;
int n, m, ans, tot, k, t;
int par[100005];
int s[100005], e[100005], v[100005], c[100005];
struct EDGE{
int s, e, v, c;
}edge[100005];
bool cmp(EDGE a, EDGE b){
if(a.v == b.v)return a.c<b.c;
return a.v < b.v;
}
void Init(int cost){
tot = 0;
t = 0;
for(int i = 1; i <= n; ++i){
par[i] = i;
}
for(int i = 1; i <= m; ++i){
edge[i].s = s[i];
edge[i].e = e[i];
edge[i].c = c[i];
if(edge[i].c==0){
edge[i].v = v[i] + cost;
}
else{
edge[i].v = v[i];
}
}
sort(edge+1, edge+m+1, cmp);
}
int Find(int node){
return par[node]==node ? node:par[node]=Find(par[node]);
}
bool Kruskal(int cost){
Init(cost);
for(int i = 1; i <= m; ++i){
int fx = Find(edge[i].s);
int fy = Find(edge[i].e);
if(fx!=fy){
if(fx>fy){
par[fx] = fy;
}
else{
par[fy] = fx;
}
tot += edge[i].v;
if(edge[i].c==0)t++;
}
}
return t>=k;
}
int main(){
scanf("%d %d %d", &n, &m, &k);
for(int i = 1; i <= m; ++i){
scanf("%d %d %d %d", &s[i], &e[i], &v[i], &c[i]);
s[i]++;
e[i]++;
}
while(l <= r){
int mid = l + r >> 1;
if(Kruskal(mid)){
l = mid+1;
ans = tot - k * mid;
}
else{
r = mid-1;
}
}
printf("%d", ans);
return 0;
}