描述
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。 题目保证有解。
输入
第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行
每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)
输出
一行表示所求生成树的边权和。
样例输入 [复制]
2 2 1
0 1 1 1
0 1 2 0
样例输出 [复制]
2
提示
数据规模和约定 ·10%:V<=10
30%:V<=15
100%:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。
Analysis
最小生成树无法控制白边的选取数量
于是我们就对白边增加/减少一定的值x
然后做Kruskal,记录白边的值
如果选取的数量大于need说明白边多了,则增加x(少选白边)
小于need说明白边少了,则减少x(多选白边)
如果刚好等于need,我们选择增加x
因为在能保证白边选取数量为need的时候,我们所选择的白边实际上已经固定
当x增加,白边相当于整体向后挪动,这样就可以使选择的黑边尽可能小
这样一来,我们选择的黑边是最小的,白边也是最小的,结果自然最优
ps在排序的时候边权相同时优先选择白边(和上述理解类似,请读者自行解决)
Code
(数组开小……很愉快)
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<1)+(res<<3)+(ch^48);
ch=getchar();
}
return f==1?res:-res;
}
const int N=5e4+10,M=2e5+10;
int n,m,need,fa[N],tot;
int s[M],t[M],c[M],w[M];
struct node{int u,v,w,c;}e[M];
inline bool cmp(const node &a,const node &b){return a.w<b.w||(a.w==b.w&&a.c<b.c);}
inline int getfa(int x){
return x==fa[x]?x:fa[x]=getfa(fa[x]);
}
inline bool check(int x){
tot=0;int cnt=0;
for(re int i=0;i<=n;++i) fa[i]=i;
for(re int i=1;i<=m;++i){
e[i].u=s[i];e[i].v=t[i];
e[i].w=w[i];e[i].c=c[i];
if(!c[i]) e[i].w+=x;
}
sort(e+1,e+m+1,cmp);
for(re int i=1;i<=m;++i){
int fu=getfa(e[i].u),fv=getfa(e[i].v);
if(fu!=fv){
fa[fu]=fv;
tot+=e[i].w;
if(!e[i].c) cnt++;
}
}
return cnt>=need;
}
int main(){
n=in;m=in;need=in;
for(re int i=1;i<=m;++i){
s[i]=in;t[i]=in;w[i]=in;c[i]=in;
}
int l=-105,r=105,ans;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) l=mid+1,ans=tot-need*mid;
else r=mid-1;
}
cout<<ans;
return 0;
}