题目描述
给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
https://www.luogu.org/problemnew/show/P2619
输入输出格式
输入格式:
第一行V,E,need分别表示点数,边数和需要的白色边数。
输出格式:
一行表示所求生成树的边权和。
说明
0:V<=10
1,2,3:V<=15
0,…,19:V<=50000,E<=100000
所有数据边权为[1,100]中的正整数。
接下来E行
每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。
首先,分析这道题,是一个关于最小生成树的问题,由于顶点数是50000,我们肯定不能用邻接矩阵,考虑邻接表。
然后,就很容易想到是要跑克鲁斯卡尔啦。
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
void kruskal(){
for(int i=1;cnt!=n-1;++i){
int r1=find(edge[i].start),r2=find(edge[i].end);
if(r1!=r2) {
f[find(r1)]=find(r2),cnt++;
if(edge[i].c==0) temp++;
ans+=edge[i].v;
}
}
}
一个裸的克鲁斯卡尔。。。。
接下来,针对next的值首先可以想到枚举,把每个白边的权值减少一定的值,看减少多少会使最小生成树的白边数正好为need
那么,优化一下,在白边权值不断减少的过程中,白边的数量是递增的,那么满足一个单调的要求,所以,这里可以用二分,二分的正确性也可以由此证明
每次枚举一个mid的值,给每个白边的权值都加上mid,如果这时白边数大于等于need,这时候对mid的值进行减小,反之增加,每次跑一遍克鲁斯卡尔,更新ans的值(ans的值要减去增加的mid的权值)
他的细节处理是非常多的,每次跑克鲁斯卡尔之前都要进行清零(考试时候少清一个调了几百年QAQ)(捂脸)
下面附上AC代码(吐槽一波考试数据的变态,十五个点连了二十万条边)
#include
#include
#include
#define FOR(i,n,m) for(int i=n;i<=m;++i) //一个喜欢用宏定义的懒人,不用在意
#define OPEN(n) freopen(n".in","r",stdin); freopen(n".out","w",stdout);
using namespace std;
const int N=102000;
int f[N],n,need,e,l,r,mid,temp,ans,_ans,cnt;
struct node {int start,end,c,v;}edge[N];
int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
bool cmp(node a,node b) {return a.v==b.v?a.c<b.c:a.v<b.v;} //排序,按顺序,如果相同,白边在前
void kruskal(){
mid=(l+r)>>1;
FOR(i,1,e) {if(edge[i].c==0) edge[i].v+=mid;}
FOR(i,1,n+1) f[i]=i;
ans=0,temp=0,cnt=0; //清零
sort(edge+1,edge+e+1,cmp);
for(int i=1;cnt!=n-1;++i){
int r1=find(edge[i].start),r2=find(edge[i].end);
if(r1!=r2) {
f[find(r1)]=find(r2),cnt++;
if(edge[i].c==0) temp++;
ans+=edge[i].v;
}
}
}
//克鲁斯卡尔啊啊啊
void in(){
scanf("%d%d%d",&n,&e,&need);
FOR(i,1,e){
scanf("%d%d%d%d",&edge[i].start,&edge[i].end,&edge[i].v,&edge[i].c);
edge[i].end++;
edge[i].start++;
}
}
void work(){
in();
l=-105,r=105;
while(l<=r){
kruskal();
if(temp>=need) l=mid+1,_ans=ans-need*mid;
else r=mid-1;
FOR(i,1,e) if(edge[i].c==0) edge[i].v-=mid;
}
printf("%d",_ans);
}
int main(){
//OPEN("tree")
//咳咳,考试的文件名
work();
return 0;
}
这是本蒟蒻的第一篇博客,各位大佬不要笑话