题目:
题解:
一开始博主想要把黑白边分开,然后sort,选择前need小的白边连起来,然后再连黑边
但这个贪心是不对的,因为黑边的大小不确定,如果目前选择了较小的白边,可能会选上更大的黑边
问题的关键在于求出的最小生成树不一定含有need条白边
1、如果白边 > need条,我们需要“拖累”白边,让ta不要加进去那么多,你光拖白边A,不拖白边B就很不公平啊,所以要拖累就把所有的白边全都拖累上
2、如果白边 < need条,我们需要“帮助”这些白边,让ta加进去的更多一些,那就给ta们全都减去一个值
我们到底是帮助还是拖累呢?具体帮多少呢?我们可以运用二分!
你可以发现这是单调的:你加入的值越多,可以加入的白边就越少;反之亦然
那就走起?最后的答案就是 sum-你添加的总值
下面是一些小细节:
当有一种情况,mid+1加入的白边少1,mid加入的白边多1,这要怎么办呢?这样的情况只会出现于一些白边和黑边的权值相等,我们在排序的时候,权值相等的白边在先就OK啦
以下为博主口胡,即使不注意这个,你依然可以过这道题
但你的程序不一定是对的,试一下(卡掉学姐的)这组数据
3 3 1
0 1 4 1
0 2 2 0
1 2 2 0
ans=6
一个小问题:你的ans值什么时候修改呢?我们说你不能对于ans取min值(why?)
如果你把这个语句(ans=min(ans,sum-need*mid))放在[选的白边小于need]的后面,你就不怕一上来白边就小于mid,选了一棵正常的最小生成树,以后的权值肯定不会被修改
如果你把这个语句放在[选的白边大于need]的后面,你就不怕一上来白边大于mid,就同上了(hack了学姐。。。)
事实上可以证明当我们限定白边的数量一定的时候,答案也是唯一的
可以避免这个问题:只有在白点相等的时候再修改ans值
但是这样的答案是错的?
为什么呢?因为这道题的题目是错的,ta其实想让你求让你求一棵最小权的至少有need条白色边的生成树。
发现这个问题之后,以上就全部是博主的自我揣测了
代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 1e9
using namespace std;
struct hh{int x,y,c,col;}e[100005],b[100005];
int f[50005],n,m,ne,sum;
int cmp(hh a,hh b){if (a.c==b.c)return a.col<b.col;else return a.c<b.c;}
int find(int x)
{
if (f[x]!=x) f[x]=find(f[x]);
return f[x];
}
int check(int mid)
{
int i,tot=0,size=0;sum=0;
for (i=0;i<n;i++) f[i]=i;
for (i=1;i<=m;i++)
{
b[i]=e[i];
if (e[i].col==0) b[i].c+=mid;
}
sort(b+1,b+m+1,cmp);
for (i=1;i<=m;i++)
{
int a=find(b[i].x),c=find(b[i].y);
if (a!=c)
{
f[a]=c;
tot++;sum+=b[i].c;
if (!b[i].col) size++;
}
if (tot==n-1) break;
}
return size;
}
int main()
{
int i,ans=INF;
scanf("%d%d%d",&n,&m,&ne);
for (i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].c,&e[i].col);
int l=-100,r=100;
while (l<=r)
{
int mid=(l+r)>>1;
if (check(mid)<ne) r=mid-1;//加入的边太少了,需要减的值很多啊
else l=mid+1,ans=sum-mid*ne;
}
printf("%d",ans);
}