题目索引:https://www.lydsy.com/JudgeOnline/problem.php?id=2654
2654: tree
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 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
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; }