[BZOJ2654] tree

2654: tree

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 610  Solved: 225
[Submit][Status][Discuss]

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

数据规模和约定

  0:V<=10

  1,2,3:V<=15

  0,..,19:V<=50000,E<=100000

  所有数据边权为[1,100]中的正整数。

Source

只想说好巧妙地想法……

对于每条白边,我们每次给他加一个值x(x∈[-101,101],因为所有边权都在这个范围之内),易看出随着x的增大,白边在MST中的数量是渐渐减少的,我们求出每条白边+x时MST中白边的最大数量,因此求出第一个小于need的x,x-1就是我们需要的x,求出此时的MST,即要求的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
struct node
{
    int u,v,w,c;
} e[100005];
int u[100005],v[100005],w[100005],c[100005],f[50005];
int n,m,need,ans,sum,cnt,l,r;
int find(int x) { return x==f[x]?x:f[x]=find(f[x]); }
bool cmp(node a,node b)
{
    return ((a.w<b.w)||((a.w==b.w)&&(a.c<b.c)));
}
bool pd(int x)
{
    ans=0;cnt=0;
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=m;i++)
    {
        e[i].u=u[i],e[i].v=v[i],e[i].w=w[i];e[i].c=c[i];
        if(!c[i])e[i].w+=x;
    }
    sort(e+1,e+m+1,cmp);
    for (int i=1;i<=m;i++)
    {
        int p=find(e[i].u),q=find(e[i].v);
        if (p!=q)
        {
            f[p]=q;
            ans+=e[i].w;
            if (e[i].c==0) cnt++;
        }
    }
    return cnt>=need;
}        
int main()
{
    scanf("%d%d%d",&n,&m,&need);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&u[i],&v[i],&w[i],&c[i]);
        u[i]++; v[i]++;
    }
    l=-101; r=101;
    while (l<=r)
    {
        int mid=(l+r)/2;
        if (pd(mid))
        {
            l=mid+1;
            sum=ans-need*mid;
        }
        else r=mid-1;
    }
    printf("%d",sum);
}
原文地址:https://www.cnblogs.com/ws-fqk/p/4660841.html