• bzoj 2654: tree


    Description

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。
    题目保证有解。

    Solution

    把边的集合看成一个序列,按照边权排序之后跑 (kruskal),就会从某个前缀中选择 (n-1) 条边
    实际上这 (n-1) 条边中的白边不等于 (need)
    如果把白边的边权同时扩大 (mid) ,那么白边之间的顺序是不变的,而白边相当于黑边整体右移了一些
    我们要做的就是在向右(左)移动尽量少的情况下,使得白边为 (need)
    最后边权和就是:最小生成树的边权和-(need*mid)
    这个东西有单调性,二分这个 (mid) 来移动即可

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    int n,m,ne,fa[N],ans=1e9;
    inline int gi(){
    	register char ch=getchar();register int str=0;
    	while(ch>'9' || ch<'0')ch=getchar();
    	while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
    	return str;
    }
    struct node{
    	int x,y,c,col,cf;
    	inline bool operator <(const node &p)const{
    		if(cf!=p.cf)return cf<p.cf;
    		return col<p.col;
    	}
    }e[N];
    inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline bool check(int mid){
    	for(int i=1;i<=m;i++)e[i].cf=e[i].col?e[i].c:e[i].c+mid;
    	for(int i=1;i<=n;i++)fa[i]=i;
    	sort(e+1,e+m+1);
    	int cr=0,tot=0,cnt=0;
    	for(int i=1;i<=m;i++){
    		int x=e[i].x,y=e[i].y;
    		if(find(x)==find(y))continue;
    		fa[find(y)]=find(x);
    		cr+=e[i].col^1;tot+=e[i].cf;
    		cnt++;
    		if(cnt==n-1)break;
    	}
    	tot-=ne*mid;
    	if(cr>=ne)return ans=tot,1;
    	return 0;
    }
    int main(){
      freopen("pp.in","r",stdin);
      freopen("pp.out","w",stdout);
      cin>>n>>m>>ne;
      for(int i=1;i<=m;i++)e[i]=(node){gi()+1,gi()+1,gi(),gi(),0};
      int l=-101,r=101,mid;
      while(l<=r){
    	  mid=(l+r)>>1;
    	  if(check(mid))l=mid+1;
    	  else r=mid-1;
      }
      cout<<ans<<endl;
      return 0;
    }
    
    
  • 相关阅读:
    day01-h1字体大小和文本居中
    js正则表达式中的
    js滚动分页原理
    在web.xml中设置全局编码
    C# 导出word 表格代码
    C# 创建单例
    Winform 异步调用2 时间
    Winform 异步调用
    c#中跨线程调用windows窗体控件
    C# 中的委托和事件
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8570473.html
Copyright © 2020-2023  润新知