题目传送门


题目描述

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


输入格式

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


输出格式

一行表示所求生成树的边权和。


样例

样例输入:

2 2 1
0 1 1 1
0 1 2 0

样例输出:

2


数据范围与提示

V≤50000,E≤100000,所有数据边权为[1,100]中的正整数。


题解

看到题很恐怖,首先,应该都能想到最小生成树,因为题目上就说了嘛~

但是还有要求,要恰好有need条白边,那么问题就复杂了。

考虑一下,如果我们对所有白边的权值都加或减一个值,然后再跑Kruskal,使用白边的个数就会发生变化,我们就找这样一个值,使使用白边个数为need,然后用这个权值进行计算,将当前情况下所有的边都加进答案,然后最后再减去need×权值,得出的结果极为答案。

发现边权为[1,100]中的正整数,所以时间复杂度O(200×E)。

然而:

发现BZOJ总是能给你意外的惊喜……

优化时间复杂度,考虑二分法:

  1.二分法QJ测试点:

  然后你能在BZOJ上而分出这个结果:完全无视白边个数这个问题,直接求最小生成树……

  

  内心过于震惊!!!

  标程:

   

  说实话,这样有些不道德,毕竟……

  

  无论如何,这道题用来检验你的最小生成树有没有打对还是好的^_^

  2.显然上面那种做法很不道德,严重QJ测试点行为!!!那么考虑二分答案进行优化,二分所有白边加或减的这个权值,如果白边个数不足need,则权值要减,反之同理。

时间复杂度O(7×E)。

那么你可能会有疑问,如果出现这样一种情况,当权值为w时,使用白边的个数<need,但是当权值为w-1时,使用白边的个数又>need了,然而题目要求我们求一棵最小权的 恰好 有need条白色边的生成树,那么这种做法的正确性又怎么论证呢?

这样思考,其实当w-1时,增加的白边个数其实也就是权值为w时把它们挤掉的那些黑边,所以其实多出来的白边都可以用黑边代替,所以就不用担心这些问题了。


代码时刻

暴力:

#include<bits/stdc++.h>
using namespace std;
struct rec
{
int s;
int t;
int c;
bool col;
}e[100001],new_e[100001];//e表示原边,new_e用来存储暂时加权值的边
int V,E,need;
int f[50001];
int val;
int sum;
void change(int x,int val)//给new_e赋值
{
new_e[x].s=e[x].s;
new_e[x].t=e[x].t;
new_e[x].c=e[x].c+(e[x].col^1)*val;//如果白边就加上val,如果是黑边则不加
new_e[x].col=e[x].col;
}
bool cmp(rec a,rec b){if(a.c==b.c)return a.col<b.col;return a.c<b.c;}//结构体排序
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}//并查集
bool judge(int x)//判断可不可以
{
int ans=0,cnt=0;
for(int i=1;i<=V;i++)f[i]=i;//并查集记得初始化
for(int i=1;i<=E;i++)change(i,x);
sort(new_e+1,new_e+E+1,cmp);
for(int i=1;i<=E;i++)
{
int xx=find(new_e[i].s);
int yy=find(new_e[i].t);
if(xx==yy)continue;
cnt++;
ans+=new_e[i].col^1;
sum+=new_e[i].c;
f[xx]=yy;
if(cnt==V-1)
if(ans>=need)return 1;
else return 0;
}
}
int main()
{
scanf("%d%d%d",&V,&E,&need);
for(int i=1;i<=E;i++)
{
scanf("%d%d%d%d",&e[i].s,&e[i].t,&e[i].c,&e[i].col);
e[i].s++;
e[i].t++;
}
int ans;
for(int i=100;i>=-100;i--)//爆力枚举答案
{
sum=0;
if(judge(i)){ans=sum-need*i;break;}
}
cout<<ans<<endl;
return 0;
}

QJ测试点,说白了就是Kruskal板子:

#include<bits/stdc++.h>
using namespace std;
struct rec
{
int s;
int t;
int c;
}e[100001];
bool col;
int V,E,need;
int f[50001];
int val;
int ans,cnt;
bool cmp(rec a,rec b){return a.c<b.c;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int main()
{
scanf("%d%d%d",&V,&E,&need);
for(int i=1;i<=E;i++)
{
scanf("%d%d%d%d",&e[i].s,&e[i].t,&e[i].c,&col);
e[i].s++;
e[i].t++;
}
for(int i=1;i<=V;i++)f[i]=i;
sort(e+1,e+E+1,cmp);
for(int i=1;i<=E;i++)
{
int x=find(e[i].s);
int y=find(e[i].t);
if(x==y)continue;
cnt++;
ans+=e[i].c;
f[x]=y;
if(cnt==V-1)break;
}
cout<<ans;
return 0;
}

正解:

#include<bits/stdc++.h>
using namespace std;
struct rec
{
int s;
int t;
int c;
bool col;
}e[100001],new_e[100001];
int V,E,need;
int f[50001];
int val;
int sum;
void change(int x,int val)
{
new_e[x].s=e[x].s;
new_e[x].t=e[x].t;
new_e[x].c=e[x].c+(e[x].col^1)*val;
new_e[x].col=e[x].col;
}
bool cmp(rec a,rec b){if(a.c==b.c)return a.col<b.col;return a.c<b.c;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
bool judge(int x)
{
int ans=0,cnt=0;
for(int i=1;i<=V;i++)f[i]=i;
for(int i=1;i<=E;i++)change(i,x);
sort(new_e+1,new_e+E+1,cmp);
for(int i=1;i<=E;i++)
{
int xx=find(new_e[i].s);
int yy=find(new_e[i].t);
if(xx==yy)continue;
cnt++;
ans+=new_e[i].col^1;
sum+=new_e[i].c;
f[xx]=yy;
if(cnt==V-1)
if(ans>=need)return 1;
else return 0;
}
}
int main()
{
scanf("%d%d%d",&V,&E,&need);
for(int i=1;i<=E;i++)
{
scanf("%d%d%d%d",&e[i].s,&e[i].t,&e[i].c,&e[i].col);
e[i].s++;
e[i].t++;
}
int lft=-105,rht=105,ans;
while(lft<=rht)//二分答案
{
sum=0;
int mid=(lft+rht)>>1;
if(judge(mid))
{
ans=sum-need*mid;
lft=mid+1;
}
else rht=mid-1;
}
cout<<ans<<endl;
return 0;
}

rp++

[BZOJ2654]:tree(Kruskal+WQS二分)的更多相关文章

  1. BZOJ2654 tree (wqs二分)

    题目描述 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解.   一个最小生成树问题,但是我们要选need条白边,我们用g(i)表示选取i条 ...

  2. Luogu P2619 [国家集训队2]Tree I(WQS二分+最小生成树)

    P2619 [国家集训队2]Tree I 题意 题目描述 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有\(need\)条白色边的生成树. 题目保证有解. 输入输出格式 输入格式 ...

  3. p2619 [国家集训队2]Tree I [wqs二分学习]

    分析 https://www.cnblogs.com/CreeperLKF/p/9045491.html 反正这个博客看起来很nb就对了 但是不知道他在说啥 实际上wqs二分就是原来的值dp[x]表示 ...

  4. [BZOJ2654] tree (kruskal & 二分答案)

    Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色 ...

  5. [CSP-S模拟测试]:赤(red)(WQS二分+DP)

    题目传送门(内部题38) 输入格式 每个输入文件包含多组测试数据.选手应当处理到文件结束($EOF$) 每一组数据包括$3$行. 第$1$行包含三个正整数$n,a,b$,表示有$n$只猫,$gyz$有 ...

  6. [BZOJ2654]tree(二分+Kruskal)

    2654: tree Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 2733  Solved: 1124[Submit][Status][Discus ...

  7. 关于WQS二分算法以及其一个细节证明

    应用分析 它的作用就是题目给了一个选物品的限制条件,要求刚好选$m$个,让你最大化(最小化)权值, 然后其特点就是当选的物品越多的时候权值越大(越小). 算法分析 我们先不考虑物品限制条件, 假定我们 ...

  8. WQS二分题集

    WQS二分,一种优化一类特殊DP的方法. 很多最优化问题都是形如“一堆物品,取与不取之间有限制.现在规定只取k个,最大/小化总收益”. 这类问题最自然的想法是:设f[i][j]表示前i个取j个的最大收 ...

  9. 2021.07.19 BZOJ2654 tree(生成树)

    2021.07.19 BZOJ2654 tree(生成树) tree - 黑暗爆炸 2654 - Virtual Judge (vjudge.net) 重点: 1.生成树的本质 2.二分 题意: 有一 ...

随机推荐

  1. POJ 2492 A Bug's Life 题解

    题面 这道题是一道标准的种类并查集: 种类并查集是给每个结点一个权值.然后在合并和查找的时候根据情况对权值来进行维护. 通过将原有的区间范围变大使并查集可以维护种类的联系: #include < ...

  2. 设备程序远程升级采用两种方式(优先采用IP方式)

    设备程序远程升级采用两种方式(优先采用IP方式): 采用应急广播TS流传输技术规范的消息内容表携带升级包数据.当辅助数据类型值为44时,消息内容表传输的数据为程序升级包. 采用IP方式传输升级包数据. ...

  3. linux源码下载

    概要:本文主要介绍ubuntu环境下,内核源码和命令源码的获取方式. 内核源码: 1.最简洁的方式,使用命令:apt-get source linux-$(uname -r).但配置的源服务器中不一定 ...

  4. docker安装应用

    1.docker安装oracle docker search oracle docker pull wnameless/oracle-xe-11g docker run -d -p 9090:8080 ...

  5. JavaSE--面向对象

    面向对象(Object Oriented) 面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节:这种思想是将数据作为第一位, ...

  6. MVC和MVVM设计模式简单理解

    1.mvc设计模式理解 Model: 模型 持有所有的数据状态和业务逻辑; 泛指数据库,链接数据库,建立数据模型 View: 视图 用来展示数据模型在页面上,泛指前端 Controller: 控制器, ...

  7. ALV打印不显示打印界面的问题

    用OO的方式screen0 不画屏幕会产生这个问题,解决办法就是不用screen0 要自己画一个区域

  8. 更新 | 2019年9月计算机二级office模拟题库

    随着2019年上半年计算机二级考试的完美落幕,紧接着的便是9月份的考试了. 到目前为止,下半年9月份计算机二级考试报名开通时间在6月前后,现在也基本结束. 2019年9月(56次)全国计算机等级考试( ...

  9. 集合类Hash Set,LinkedHashSet,TreeSet

    集合(set)是一个用于存储和处理无重复元素的高效数据结构.映射表(map)类似于目录,提供了使用键值快速查询和获取值的功能. HashSet类是一个实现了Set接口的具体类,可以使用它的无参构造方法 ...

  10. 2019-11-29-Roslyn-通过-Nuget-管理公司配置

    title author date CreateTime categories Roslyn 通过 Nuget 管理公司配置 lindexi 2019-11-29 08:58:52 +0800 201 ...