UOJ Logo kczno1的博客

博客

CF 125E. MST Company

2017-04-15 10:23:43 By kczno1

容易想到二分。
二分一个值w,将与1相连的边全部减去w,之后做mst。
那么w越大,mst选择的与1相连的边的个数(记作num)就越大。
那么二分出使得num=k的w即可。
因为我们保证了结果是原mst-k*w后最小的,
所以也一定保证了原mst最小。

但问题是,对于一个w,num的值是不确定的,因为最小生成树有很多选择。
网上有人说把二分的w改成double即可,我认为这是错的。
double其实没什么意义,由于边权是整数,1.1和1.2做出来的mst一定是一样的。

其实对于一个w,num的值应该是一个区间[l,r]。
我们二分出使得r>=k的最小的w,那么[l,r]一定包含k,因为w-1的r<k,
这时我们再做一遍mst,使得恰好选k条边即可。

#include<bits/stdc++.h>
using std::sort;

const int N=5005,M=100000,U=100001;
int n,m,k,i;
struct edge
{
    int x,y,id,w;
    bool is;
    bool operator <(const edge &i)const
    {
        if(w!=i.w) return w<i.w;
        return is; 
    }
}q[M+3];
int st[N],top;
int f[N];
int find(int x)
{
    return x==f[x]?x:f[x]=find(f[x]);
}
int check(int w,bool can)
{
    for(i=1;i<=m;++i) 
    if(q[i].is)q[i].w-=w;

    int ans=0,x,y;
    sort(q+1,q+m+1);
    for(i=1;i<=n;++i) f[i]=i;
    for(top=0,i=1;i<=m;++i)
    {
        x=find(q[i].x);y=find(q[i].y); 
        if(x==y) continue;
        if(!can&&ans+q[i].is>k) continue;
        f[x]=y;
        st[++top]=q[i].id;
        ans+=q[i].is;
    } 

    for(i=1;i<=m;++i)     
    if(q[i].is)q[i].w+=w;

    return ans;
} 

#define mid (l+r>>1)

int main()
{ freopen("1.in","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    for(i=1;i<=m;++i)
    {
        scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].w);q[i].id=i;
        q[i].is=(q[i].x==1)||(q[i].y==1);
    }
    if(check(U,1)<k||check(-U,1)>k||top<n-1) printf("-1\n");
    else
    {
        int l=-U,r=U;
        while(l+1!=r)
        if(check(mid,1)<k) l=mid;
        else r=mid;
        check(r,0);
        printf("%d\n",n-1);
        for(;top;--top) printf("%d ",st[top]);
    }
}

评论

EndSaH
hack: ``` 7 9 3 1 2 8 1 7 4 1 4 3 1 5 6 3 6 1 3 4 4 4 7 1 4 6 3 5 6 1 ``` 只输出了 5 条边

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。