P2619 [国家集训队2]Tree I

题目链接

题解:

今天考到了这道题,一开始看到最小生成树,但越做发现越不简单,想着想着又想到九霄云外去了,竟然先跑一遍mst,用树剖维护最小生成树...但其实c的范围已经在提醒我们可以用二分答案。其实想一想还是很简单的,就是二分所有白边可能减少的边权值,范围在[-1000-1000]之间,(这题还要小一点[-100-100])然后每一次做一下kruskal即可。判断是否满足need,一直二分答案即可。

注意此题的坑点,开始按边权排序的时候,要注意:如果边权相同,要优先选白边。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
struct node{
    int x,y,val,col;
}tree[maxn*4];
int n,m,need;
int tot,cnt,sum,ans;
int fa[maxn];
int get(int x){
    if(x==fa[x]) return x;
    else return fa[x]=get(fa[x]);
}
bool cmp(node a,node b){
    if(a.val==b.val) return a.col<b.col;
    else return a.val<b.val;
}
int main(){
    freopen("e.in","r",stdin);
    freopen("e.out","w",stdout);
    scanf("%d%d%d",&n,&m,&need);
    for(int i=1;i<=m;i++) scanf("%d%d%d%d",&tree[i].x,&tree[i].y,&tree[i].val,&tree[i].col);    
    int l=-1005,r=1005;
    while(l<r){
        int mid=l+(r-l)/2;
        cnt=tot=sum=0;
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) if(tree[i].col==0) tree[i].val+=mid;
        sort(tree+1,tree+1+m,cmp);
        for(int i=1;i<=m;i++){
            int f1=get(tree[i].x);
            int f2=get(tree[i].y);
            if(f1!=f2){
                fa[f1]=f2;
                sum+=tree[i].val;
                tot++;
                if(tree[i].col==0) cnt++;
                if(tot==n-1) break;
            }
        }
        if(cnt>=need){
            l=mid+1;
            ans=sum-need*mid;
        }
        else r=mid;
        for(int i=1;i<=m;i++) if(tree[i].col==0) tree[i].val-=mid;
    }
    printf("%d\n",ans);
    return 0;
} 
View Code

 

posted @ 2019-10-10 21:52  JBLee  阅读(124)  评论(0编辑  收藏  举报