tree (一本通练习||清华集训互测)

tree

内存限制:512 MiB 时间限制:3000 ms 标准输入输出
题目类型:传统 评测方式:文本比较
 

题目描述

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

输入格式

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

输出格式

一行表示所求生成树的边权和。
V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

样例

样例输入

2 2 1
0 1 1 1
0 1 2 0

样例输出

2

数据范围与提示

原数据出错,现已更新 by liutian,但未重测---2016.6.24

也是一个做法比较玄学的题

二分答案,考虑我们往白边上加值或者减值,那么就会对应的少选白边或者少选黑边

那么如果当前  白边+mid如果kuskal选择白边比need多就继续往上面加值,如果选择白边比need少就往下减值

因为kuskal保证图一定连通,并且代价最小。所以保证了正确性

#include<bits/stdc++.h>
#define ll long long
#define A 10000000
using namespace std;
struct edge{
    ll x,y,z,id;
    ll flag;
}e[A];
ll n,m,need,fa[A],zong,ans,end[A];
inline ll read()
{
    ll f=1,x=0;char c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return f*x;
}
ll find(ll x)
{
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
void hebing(ll x,ll y)
{
    x=find(x),y=find(y);    if(x!=y) fa[y]=x;
}
bool cmp(edge a,edge b){return (a.z==b.z)?(a.flag<b.flag):(a.z<b.z);}
inline void kuskar(ll mid)
{
    for(ll i=0;i<=n;i++)
        fa[i]=i;
    zong=0,ans=0;
    for(ll i=1;i<=m;i++)
    if(!e[i].flag)
        e[i].z+=mid;
    sort(e+1,e+m+1,cmp);
    for(ll i=1;i<=m;i++)
    {
        if(find(e[i].x)!=find(e[i].y))
        {
            if(!e[i].flag)
                zong++;
            hebing(e[i].x,e[i].y);
            ans+=e[i].z;
        }
    }
    for(ll i=1;i<=m;i++)
        if(!e[i].flag)
            e[i].z-=mid;
}
int main()
{
    ll tot=0;
    n=read(),m=read(),need=read();
    for(ll i=1;i<=m;i++)
        e[i].x=read(),e[i].y=read(),e[i].z=read(),e[i].flag=read();
    ll l=-100,r=100;
    while(l<=r)
    {
        ll mid=(l+r)>>1;
        kuskar(mid);
        if(zong>=need) l=mid+1,tot=ans-need*mid;
        else r=mid-1;
    }
    cout<<tot<<endl;
}
View Code

猜你喜欢

转载自www.cnblogs.com/znsbc-13/p/11202606.html
今日推荐