BZOJ2654 tree

一开始打了个贪心,求最小生成树,白边多就把权值最大的白边干掉,白边少就把权值最大的黑边干掉,因为数据肯定有解嘛。结果大点TLE,小点只过了一个。事实证明,贪心有时真的不能瞎用。

其实这个二分还是挺有道理的,给白边加权值,则入选白边会减少,给白边减权值,则入选白边增加,二分枚举这个权值,白边多了则向大枚举,白边少了则向小枚举,思路蛮简单的。

剩下的就是二分的细节了。试了无数遍,终于找到了最合适的二分方法,而且等号最好放在大于号那里,因为恰好有need条白边以后ans还不一定最优,应该尝试着在白边不变的基础上,把更优秀的黑边加进来,需要把白边排斥一下。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
struct EDGE{
    int st,ed,nex,val,col;
}edge[2005000];int num,first[500050];
int read(){
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
int n,m,need,ans,ans2,fa[500500];
void add(int st,int ed,int val,int col){
    edge[++num].st=st;
    edge[num].ed=ed;
    edge[num].val=val;
    edge[num].col=col;
    edge[num].nex=first[st];
    first[st]=num;
}
bool camp(EDGE a,EDGE b){
    if(a.val==b.val) return a.col>b.col;
    return a.val<b.val;
}
int get(int x){
    return x==fa[x]?x:fa[x]=get(fa[x]);
}
bool check(int x){
//    cout<<"mid="<<x<<endl;
    int color=0,sum=0;
    ans=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=num;i++) 
        if(edge[i].col) edge[i].val+=x;
    sort(edge+1,edge+1+num,camp);
    for(int i=1;i<=num;i++){
        int x=edge[i].st,y=edge[i].ed;
        x=get(x);y=get(y);
        if(x==y) continue;
        fa[x]=y;
        color+=edge[i].col;
        ans+=edge[i].val;
        sum++;
        if(sum==n-1) break;
    }
    for(int i=1;i<=num;i++)
        if(edge[i].col) edge[i].val-=x;
/*    cout<<color<<" "<<endl;
    for(int i=1;i<=num;i++){
        cout<<edge[i].val<<" ";
    }cout<<endl;*/
//    fgetc(stdin);
    return color>=need;
}
int main(){
    n=read();m=read();need=read();
    for(int i=1;i<=m;i++){
        int st=read(),ed=read(),val=read(),col=read();
        add(st+1,ed+1,val,col^1);
    }
    int l=-102,r=102;
    while(l<r){
    //    cout<<l<<" "<<r<<endl;
        int mid=l+r>>1;
        if(check(mid)){ 
            l=mid+1;
            ans2=ans-need*mid;
        }
        else r=mid;

    }printf("%d",ans2);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Yu-shi/p/11177856.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值