您的位置:首页 > 其它

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

2017-10-30 14:24 399 查看

题目概述

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

解题报告

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

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

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

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

示例程序

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: