BZOJ P2654 tree 【最小生成树】【二分答案】

题目索引:https://www.lydsy.com/JudgeOnline/problem.php?id=2654

2654: tree

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 2768   Solved: 1136
[ Submit][ Status][ Discuss]

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

HINT

原数据出错,现已更新 by liutian,但未重测---2016.6.24

Source


题目分析:

       求满足题意的最小生成树的权值->首先要满足要求->选出的最小生成树有need条白边->可是选出的最小生成树不一定有need条白边这么办->对于每一次Kruskal->我们可以得到当前最小生成树中白边的数量Cnt->如果Cnt>need->白边的数量比所需要的多了->白边的权值太小->使每一条白边都加去一个值->使得有可能下一次跑Kruskal统计出来的Cnt减少->如果出现Cnt<need->白边的数量比所需要的少了->白边的权值太大->使每一条白边都减去一个值->使得有可能下一次跑Kruskal统计出来的Cnt增多->我们该如何修改白边的权值->白边的权值修改多少->得解->二分答案+生成树。

代码如下:

        

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define DB double
#define SG string
#define LL long long
#define Fp(A,B,C,D) for(A=B;A<=C;A+=D)
#define Fm(A,B,C,D) for(A=B;A>=C;A-=D)
#define Clear(A) memset(A,0,sizeof(A))
using namespace std;
const LL Max=1e5+5;
const LL Mod=1e9+7;
const LL Inf=1e18;
struct Node{
    LL C,X,Y,V;
    void Get(LL A,LL B,LL P,LL Q){
        C=A;X=B;Y=P;V=Q;
    }
}G[Max<<1];
LL N,M,W,Ans,Cnt,Tot,Left=-105,Mid,Right=105,C[Max<<1],X[Max<<1],Y[Max<<1],V[Max<<1],F[Max<<1];
inline LL Read(){
    LL X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}
inline void Write(LL X){
    if(X<0)X=-X,putchar('-');
    if(X>9)Write(X/10);
    putchar(X%10+48);
}
bool Cmp(Node A,Node B){
    return A.V==B.V?A.C<B.C:A.V<B.V;
}
LL Find(LL X){
    return F[X]==X?X:Find(F[X]);
}
void Kruskal(){
    LL I,J,K;Ans=Cnt=0;
    Fp(I,1,N,1){
        F[I]=I;
    }
    Fp(I,1,M,1){
        G[I].Get(C[I],X[I],Y[I],V[I]);
        if(!G[I].C){
            G[I].V-=Mid;
        }
    }sort(G+1,G+1+M,Cmp);
    Fp(I,1,M,1){
        LL P=Find(G[I].X);
        LL Q=Find(G[I].Y);
        if(P!=Q){
            F[P]=Q;
            Ans+=G[I].V;
            if(!G[I].C){
                Cnt++;
            }
        }
    }
}
int main(){
    LL I,J,K;
    N=Read(),M=Read(),W=Read();
    Fp(I,1,M,1){
        X[I]=Read()+1;
        Y[I]=Read()+1;
        V[I]=Read();
        C[I]=Read();
    }
    while(Left<=Right){
        Mid=Left+Right>>1;
        Kruskal();
        if(Cnt>=W){
            Right=Mid-1;
            Tot=Ans+W*Mid;
        } else {
            Left=Mid+1;
        }
    }Write(Tot);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanyidu/article/details/79978616