Luogu P2619 [国家集训队2]Tree I
题目描述
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
题目保证有解。
思路
其实这题我好像在7月份就在扬州听过
思想没有问题,但由于当时太弱了,没能做出来
所以清flag时先挑一个软柿子捏
将每一个白边加上一个权值,跑kruskal,二分此权值,直到生成树正好有need条白边。
code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100000+10;
inline int read()
{
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
struct node{
int from,to,v;
bool color;
}_map[MAXN],edge[MAXN];
int v,e,need,cnt,num,sum,ans;
int fa[MAXN];
void addedge(int s,int t,int c,int col)
{
edge[++cnt].color=col;
edge[cnt].from=s;
edge[cnt].to=t;
edge[cnt].v=c;
}
bool cmp(node a,node b)
{
return (a.v==b.v)? a.color<b.color : a.v<b.v;
}
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void kruskal()
{
for(int i=1;i<=v;++i){
fa[i]=i;
}
num=0,sum=0;
int nn=0;
sort(edge+1,edge+e+1,cmp);
for(int i=1;nn<v-1;++i){
int xx=find(edge[i].from),yy=find(edge[i].to);
if(xx==yy){
continue;
}
if(xx!=yy){
nn++;
fa[xx]=yy;
if(!edge[i].color){
num++;
}
sum+=edge[i].v;
}
}
}
bool check(int mid)
{
memcpy(edge,_map,sizeof(_map));
for(int i=1;i<=e;++i){
if(!edge[i].color){
edge[i].v+=mid;
}
}
kruskal();
return num>=need;
}
void midfind()
{
int mid,l=-120,r=120;
while(l<=r){
// cout<<"midfind\n";
mid=(l+r)/2;
if(check(mid)==1){
ans=sum-mid*need;
l=mid+1;
}else{
r=mid-1;
}
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
cin>>v>>e>>need;
for(int i=1;i<=e;++i){
int s,t,c,col;
s=read(),t=read(),c=read(),col=read();
addedge(s+1,t+1,c,col);
}
memcpy(_map,edge,sizeof(_map));
midfind();
cout<<ans;
return 0;
}