世界真的很大
今天的风儿甚是喧嚣,豆大的雨滴悄咪咪地往下落
这也是一道挺有意思的题
难点在于推导,并不在于代码
主要是锻炼对模板的熟悉程度,和思维难度
但只是马马虎虎地靠感觉推导其实也并没有那么困难
只是认真想来有一点细思恐极的味道
还是看一下题为好
description
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
input
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
output
一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。
读完题就该想到最小生成树了
但关键是怎么把握白色边的个数
Kruscal算法的基本原理是边按权值排序,从小往大凑满n-1条边
如果我们能让白色边的排序稍微靠后,那取到白色边的可能就会降低,起码白色边的数量会小于等于之前的数量
让白色边的排序靠后的办法,无非也是给所有的白色边增加权值了
因为随增加权值的增加,白色边的数量单调递减,所以具有可二分的性质
于是乎我们采用二分的办法来逐渐逼近need值,算出那时的最小生成树的权值和
姑且看来就是这样了,代码也很好写
但还有点点
假设有一串增加的权值,使得白色边的数量都是need,那它们所对应的最小生成树的值是相等的吗?
现在我和大佬讨论后,有两种学说
1。是相等的。因为在增加的权值增加时,边排序里,白色边的相对位置肯定是向后挪的。如果使最小生成树的结构不同的话,必然有不属于之前的最小生成树的边加入,也就是说白色边增加权值后大于了新的边,使其相对位置提前。而由于白色边自己的相对位置是不变的,所以提前的只能是黑色边。这条新的黑色边不属于之前的最小生成树,即白色边向后挪的过程中超出了之前的最小生成树的n-1条边,那白色边的数量必然减少,而白色边的数量不变,所以矛盾,所以就是最小生成树的结构不变。
2。随着增加的权值的增加,白色边数量不变的情况下,最小生成树的权值是递增的。因为如果结构改变,使得一条白色边取不到的话,就必然有一条边来替代它的位置,白色边的相对位置不变,所以只能是黑色边。而白色边的数量不变,所以末尾必然有一条白色边补充。白色边的相对位置不变,所以后面补充的白色边的权值一定大于之前白色边的权值。所以是递增的、
这个嘛,两边都有道理,不管是哪边正确,只需要在二分的时候尽可能往左边靠就行了,保证取到need值的是增加的权值最少的点
完整代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
struct edge
{
int u,v,w,clr;
}ed[500010];
int n,m,ned,sum=0,ans,fa[500010];
bool cmp(const edge &a,const edge &b)
{
return a.w<b.w;
}
int getfather(int x)
{
if(x==fa[x]) return x;
return fa[x]=getfather(fa[x]);
}
int Kruscal(int x)
{
int bns=0,tot=0;sum=0;
for(int i=0;i<n;i++) fa[i]=i;
sort(ed+1,ed+m+1,cmp);
for(int i=1;i<=m;i++)
{
int x=getfather(ed[i].u);
int y=getfather(ed[i].v);
if(x!=y)
{
tot++;
fa[x]=y;
sum+=ed[i].w;
if(!ed[i].clr) bns++;
}
if(tot==n-1) break ;
}
sum-=x*bns;
for(int i=1;i<=m;i++)
if(!ed[i].clr) ed[i].w-=x;
return bns;
}
int check(int x)
{
for(int i=1;i<=m;i++)
if(!ed[i].clr) ed[i].w+=x;
return Kruscal(x);
}
int main()
{
scanf("%d%d%d",&n,&m,&ned);
for(int i=1;i<=m;i++)
scanf("%d%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w,&ed[i].clr);
int lf=-10000,rg=10000;
while(lf<=rg)
{
int mid=(lf+rg)>>1;
int tmp=check(mid);
if(tmp>=ned)
{
ans=sum;
lf=mid+1;
}
else rg=mid-1;
}
printf("%d",ans);
return 0;
}
嗯,就是这样