您的位置:首页 > 其它

[bzoj2654][最小生成树][二分]tree

2017-12-14 14:01 393 查看
Description

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。 题目保证有解。

Input

第一行V,E,need分别表示点数,边数和需要的白色边数。

接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

Output

一行表示所求生成树的边权和。 V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

Sample Input

2 2 1

0 1 1 1

0 1 2 0

Sample Output

2

题解

神题呐不会啊只能膜题解了。。

如果直接对原树进行kruskal的话,求出来的白边可能<need也可能>need的对不

那么怎么人为控制白边在树里的数目同时保证黑边选择最小呐?

还是最小生成树,只不过我们二分一个值mid,每次check的时候每条白边加上这个mid值,这样可以保证白边在生成树里一定是单调不下降或者单调不上升的,而这个时候的黑边同样有保证一定是最小的。

跑kruskal的时候判一下白边的边数,如果出来的边数>=need就是正确状态,那么树里的白边就需要减小,这时候mid往大的二分

小于need的情况就往小的二分

答案继承的时候需要减去mid*need这么多,因为白边至少都多了这么多的权嘛

一个特别的地方:题里可能有权相等的黑白边,这样kruskal排序的时候黑白边顺序是不定的。那这样跑出来的边数就可能小于need而wa掉。所以我们排序的时候当边权相等的时候再按颜色排,白色在前

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node
{
int x,y,c,op;
}a[211000],e[211000];
int fa[111000];
int findfa(int x)
{
if(fa[x]!=x)fa[x]=findfa(fa[x]);
return fa[x];
}
int n,m,nd,cnt;
bool cmp(node n1,node n2)
{
if(n1.c!=n2.c)return n1.c<n2.c;
else return n1.op<n2.op;
}
bool chk(int p)
{
for(int i=1;i<=m;i++)
{
e[i]=a[i];
if(a[i].op==0)e[i].c+=p;
}
for(int i=1;i<=n;i++)fa[i]=i;
sort(e+1,e+1+m,cmp);
int tmp=n,op=0;cnt=0;
for(int i=1;i<=m;i++)
{
int p=findfa(e[i].x),q=findfa(e[i].y);
if(p!=q)
{
fa[p]=q;
cnt+=e[i].c;tmp--;
if(e[i].op==0)op++;
if(tmp==1)break;
}
}
if(op>=nd)return true;
else return false;
}
int main()
{
scanf("%d%d%d",&n,&m,&nd);
for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].c,&a[i].op);a[i].x++;a[i].y++;}
int l=-150,r=150,ans;
while(l<=r)
{
int mid=(l+r)/2;
if(chk(mid))
{
ans=cnt-nd*mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: