Evanyou Blog 彩带

  BZOJ传送门COGS传送门

tree

Description

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

Input

第一行V,E,need分别表示点数,边数和需要的白色边数。
接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

Output

一行表示所求生成树的边权和。

Sample Input

2 2 1
0 1 1 1
0 1 2 0

Sample Output

2

Hint

V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。


  分析:

  在大鸡哥的博客里看到的,一开始的分析就是直接一波玄学排序然后暴力Kruskal+一堆判断,然后自己把自己推翻了。。。

  还是看了大鸡哥的博客才懂得。大鸡哥的博客里讲的很清晰了,而且还讲到了COGS上和BZOJ上数据不同的问题。就推荐一下大鸡哥的博客吧。

  Code:

//It is made by HolseLee on 1st June 2018
//BZOJ 2654/COGS 1764
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
const int M=1e5+7;
int n,m,k,fa[N],num,ans;
struct Node{
int from,to,val,c,id;
}edge[M];
inline int read()
{
    char ch=getchar();int num=0;bool flag=false;
    while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
    while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
    return flag?-num:num;
}
inline bool cmp(Node a,Node b)
{return a.c==b.c?a.id<b.id:a.c<b.c;}
inline int find(int x)
{return fa[x]==x?x:fa[x]=find(fa[x]);}
inline int check(int ka)
{
    for(int i=1;i<=m;i++)
    edge[i].c=edge[i].val+(edge[i].id^1)*ka;
    sort(edge+1,edge+m+1,cmp);
    for(int i=0;i<n;i++)fa[i]=i;
    int cnt=0,tot=0;num=0;
    for(int i=1;i<=m;i++){
        int x=find(edge[i].from);
        int y=find(edge[i].to);
        if(x!=y){fa[y]=x;tot++;
        cnt+=(edge[i].id==0?1:0);
        num+=edge[i].c;}
        if(tot==n-1)break;}
    return cnt;
}
int main()
{
    freopen("nt2012_tree.in","r",stdin);
    freopen("nt2012_tree.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&edge[i].from,&edge[i].to);
        scanf("%d%d",&edge[i].val,&edge[i].id);}
    int L=-100,R=100,mid;
    while(L<=R){
        mid=(L+R)/2;
        if(check(mid)>=k)ans=num-k*mid,L=mid+1;
        else R=mid-1;}
    printf("%d",ans);return 0;
}
原文地址:https://www.cnblogs.com/cytus/p/9123721.html