bzoj2654: tree(二分+最小生成树)

来源:互联网 发布:dota2个人数据查询 编辑:程序博客网 时间:2024/05/16 05:19

题目传送门
神题。

解法:
想了我一个小时就是想不出来怎么做。
上来看看硬做行不行好像不行。
想了想二分边权和。
还是不行。。
想了很久怎么做。想不出来。
%题解!!

哇神方法。
当所有白色的边都加上一个东西的时候。
那么对于最后的方案白色的边因为权大了所以势必要减少。
当所有白色的边都减去一个东西的时候。、
那么对于最后的方案白色的边因为权小了所以势必要增多。
所以就二分这个东西呀。
然后去跑最小生成树。
跑出来的结果如果白色边的条数>=need说明白色的多了。
那么我们让他的数目减少一点,那么增加的权就应该多一点。
所以往大的二分。
如果白色边的条数

#include<cstdio>#include<cstring>#include<cstdlib>#include<iostream>#include<algorithm>#include<cmath>#include<queue>using namespace std;struct node {int x,y,c,t;}a[110000];int len;bool cmp(node n1,node n2) {if(n1.c==n2.c)return n1.t<n2.t;return n1.c<n2.c;}int fa[51000];int findfa(int x) {if(fa[x]!=x)fa[x]=findfa(fa[x]);return fa[x];}int main() {    int n,m,K;scanf("%d%d%d",&n,&m,&K);int l=-101,r=101,mid,ans=0;    for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].c,&a[i].t);a[i].x++;a[i].y++;}    while(l<=r) {        mid=(l+r)/2;for(int i=1;i<=m;i++)if(a[i].t==0)a[i].c+=mid;         sort(a+1,a+1+m,cmp);int t=0,sum=0,k=0;        for(int i=1;i<=n;i++)fa[i]=i;        for(int i=1;i<=m;i++) {            int xx=findfa(a[i].x),yy=findfa(a[i].y);            if(xx!=yy) {fa[xx]=yy;t++;sum+=a[i].c;if(a[i].t==0)k++;if(t==n-1)break;}        }for(int i=1;i<=m;i++)if(a[i].t==0)a[i].c-=mid;        if(k>=K) {l=mid+1,ans=sum-K*mid;}        else r=mid-1;    }    printf("%d\n",ans);    return 0;}