跪神题,跪秒题大神zy!
很多人的第一想法就是选前need小的白边,再放黑边,很不幸这是错的
正确解法很有启发性。
如果我们按照正常的选边来做最小生成树,如果选出的白边大于need条,我们就需要少选一些的白边,否则我们就需要多选一些白边。
如何才能少选一些白边呢?我们可以该白边全部加上一个正数,同理我么可以多选一些白边。
这样我们就可以二分了!
但是这还有一些问题,假设我们二分得到x,其对应的生成树大小为need+1,而x+1对应的大小为need-1,答案是什么?
其实答案就是x下生成树大小减去need*x
为什么?
考虑被删除的那两条边,由于在值+1后被删除,一定有相等大小的黑边可以代替此白边,也就是说我们可以恰好选出need条白边。
得证!
再跪大神zy!
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 100005;
int fa[Maxn];
int n,m,need;
int L,R,Mid;
int ans,sum,K;
int m1,m2,i;
struct EDGE
{
int x,y,len,col;
void read()
{
scanf("%d%d%d%d",&x,&y,&len,&col);
x++; y++;
}
bool operator <(const EDGE &a)const
{
return (len < a.len) || (len == a.len && col<a.col);
}
} edge[Maxn], e[Maxn];
int gf(int x){
int xx=x, xxx;
while (xx!=fa[xx]) xx=fa[xx];
while (x!=xx) xxx=x, x=fa[x], fa[xxx]=xx;
return xx;
}
bool Judge(int dlt){
for (i=1;i<=m;i++){
e[i] = edge[i];
if (e[i].col==0) e[i].len += dlt;
}
sort(e+1,e+m+1);
sum = 0; K = 0;
for (i=1;i<=n;i++) fa[i] = i;
for (i=1;i<=m;i++){
m1 = gf(e[i].x);
m2 = gf(e[i].y);
if (m1!=m2){
fa[m1] = m2;
sum += e[i].len;
if (e[i].col==0) K++;
}
}
return K >= need;
}
int main(){
freopen("2654.in","r",stdin);
freopen("2654.out","w",stdout);
scanf("%d%d%d",&n,&m,&need);
for (i=1;i<=m;i++)
edge[i].read();
L = 0; R = 200;
while (L<=R){
Mid = (L+R)/2 - 100;
if (Judge(Mid)){
L = Mid+100 + 1;
ans = sum - Mid*need;
}
else R = Mid+100 - 1;
}
printf("%d\n",ans);
return 0;
}