【wqs二分+最小生成树】BZOJ2654[tree]题解

题目概述

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

解题报告

可怕的题目……首先我们可以先求出一棵最小生成树,但是不一定满足 K 条白色边。

这怎么办呢?容易想到提高(降低)白色边的优先级,从而多选(少选)白色边。

于是我们可以将白色边的边权全加上 mid m i d (这样白色边之间的优先级保持不变),然后重新求最小生成树,如果满足要求说明 mid m i d 可行。而由于 mid m i d 对白色边的影响是单调的,所以可以二分。

还需要注意,Kruskal对边排序时,应该双关键字排序(白色边比边权相同的黑色边优先)。

2018.3.15UPD:这其实就是wqs二分,但是我不会证明斜率单调QAQ。

示例程序

#include<cstdio>
#include<algorithm>
#define fr first
#define sc second
using namespace std;
typedef pair< pair<int,int>,pair<int,int> > Edge;
const int maxn=50000,maxe=100000;

int n,m,K;Edge e[maxe+5];
int father[maxn+5],now,ans;

#define val(e) (e.sc.fr+now*(e.sc.sc^1))
inline bool cmp(Edge a,Edge b) {return val(a)<val(b)||val(a)==val(b)&&a.sc.sc<b.sc.sc;}
int getfa(int x) {if (father[x]==x) return x;return father[x]=getfa(father[x]);}
inline bool check(int mid)
{
    int tot=0;ans=0;now=mid;for (int i=0;i<n;i++) father[i]=i;sort(e+1,e+1+m,cmp);
    for (int i=1;i<=m;i++)
    {
        int fx=getfa(e[i].fr.fr),fy=getfa(e[i].fr.sc);if (fx==fy) continue;
        ans+=val(e[i]);tot+=!e[i].sc.sc;father[fx]=fy;
    }
    return tot>=K;
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d%d%d",&n,&m,&K);
    for (int i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].fr.fr,&e[i].fr.sc,&e[i].sc.fr,&e[i].sc.sc);
    int L=-105,R=105;
    for (int mid=L+(R-L>>1);L<=R;mid=L+(R-L>>1)) if (check(mid)) L=mid+1; else R=mid-1;
    return check(R),printf("%d\n",ans-R*K),0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值