首先让我们来介绍Krukal算法,他是一种用来求解最小生成树问题的算法,首先把边按边权排序,然后贪心得从最小开始往大里取,只要那个边的两端点暂时还没有在一个联通块里,我们就把他相连,只要这个图里存在最小生成树我们就一定可以找到他。(证明:首先如果我们没有选最小的边,那么他一定可以踢掉其他的边来使生成树更小,于是最小一定取,那么接下来能取的边同理,以此类推我们证毕。)

这个算法其实不要紧,但是他这种利用边的置换的思想,与得到最小生成树的定性,才是我们真正的收获。

【BZOJ 3654】tree

这道题在思路上还是很清晰的,他保证存在了,那么我们就是找最小的就可以。那么我们先把边排序,跑Kruskal,然后通过二分给白边加权,然后再求最小生成树,慢慢使我们的白边树逼近需要就是了,因为他说一定存在,所以你二分到小一点就多,大一点就少的情况就可以看你取边顺序直接取一个值就好了。

#include <cstdio>
#include <algorithm>
inline void read(int &sum){
    register char ch=getchar();
    for(sum=0;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';sum=(sum<<1)+(sum<<3)+ch-'0',ch=getchar());
}
const int N=50010;
const int M=100010;
struct E{
    int a,b,w,c;
}e[M];
int f[N],h[2];
inline int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
}
int n,m,need;
inline bool comp(E a,E b){
    return a.w+h[a.c]<b.w+h[b.c]||(a.w+h[a.c]==b.w+h[b.c]&&a.c<b.c);
}
inline int get_ans(int &get){
    for(int i=1;i<=n;i++)f[i]=i;
    std::sort(e+1,e+m+1,comp);
    register int x,y,w,c,hav=0,ret=0,whi=0;
    for(int i=1;i<=m;i++){
        x=e[i].a+1,y=e[i].b+1,w=e[i].w,c=e[i].c;
        if(find(x)==find(y))continue;
        f[find(x)]=find(y);
        ret+=w+h[c],whi+=c,hav++;
        if(hav==n-1)break;
    }
    get=hav-whi;
    return ret;
}
int main(){
    read(n),read(m),read(need);
    for(int i=1;i<=m;i++)
        read(e[i].a),e[i].a++,read(e[i].b),e[i].b++,read(e[i].w),read(e[i].c);
    int mid,l=-100,r=100,ans,get;
    while(l<=r){
        mid=(l+r)>>1,h[0]=mid;
        int ret=get_ans(get);
        if(get>=need)
            ans=ret-need*h[0],l=mid+1;
        else r=mid-1;
    }
    printf("%d",ans);
    return 0;
}
【BZOJ 3654】tree

相关文章:

  • 2021-11-28
  • 2022-03-02
  • 2021-06-10
  • 2021-07-01
  • 2021-09-20
  • 2021-05-29
  • 2022-02-27
  • 2021-08-03
猜你喜欢
  • 2022-01-10
  • 2022-02-24
  • 2021-09-15
  • 2022-01-27
  • 2022-12-23
  • 2021-12-02
  • 2021-09-05
相关资源
相似解决方案