[火星补锅] 水题大战Vol.2 T2 && luogu P3623 [APIO2008]免费道路 题解
前言:
如果我自己写的话,或许能想出来正解,但是多半会因为整不出正确性而弃掉。
解析:
这题算是对Kruskal的熟练运用吧。
要求一颗生成树。也就是说,最后的边数是确定的。
首先我们容易想到一个策略:
先跑Kruskal,优先选k条石子路,剩下的选水泥路。
但是这样做显然是错误的。
因为,当随便选了k条石子路后,可能出现:
发现无论怎么选(n-1-k)条水泥路,也无法使图连通。如果这时选一条石子路,就可以保证连通性。
但是,发现这时已经选满了k条石子路,就没法再选石子路了。
我们可以通过设计一个策略来使这种情况不发生。
首先,我们可以优先用水泥路跑Kruskal,这时如果有一条石子路,如果不加上它,就无法保证连通性,那就将其打上标记,意思是这条石子路一定会出现在最后的答案里面。
第二遍Kruskal,先将上次打过标记的石子路加入并查集。然后,将石子路补成k条。
第三遍,补上水泥路。
这个策略为什么是正确的呢?
首先,可以发现,(设第一遍Kruskal找出了cnt条石子路)
执行第一遍Kruskal后,假如存在生成树,那么我们必然可以通过cnt条石子路+一些水泥路的方式来找到一颗生成树。
那么,此时我们再选一些石子路,相当于把一些水泥路换成了石子路。假如原来是有方案的,那么后面一定是有方案的。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm=100000+10,maxn=20000+10;
#define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? EOF : *p1++) : *p1++)
#define read() ({ register int x = 0, f = 1; register char c = gc(); while(c < '0' || c > '9') { if (c == '-') f = -1; c = gc();} while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc(); f * x; })
char buf[1 << 20], *p1, *p2;
struct node{
int from,to,op,flag;
}b[maxm],ans[maxm];
int n,m,k,cnt,tot;
int fa[maxn];
bool cmp1(node x,node y){
return x.op>y.op;
}
int find(int x){
return fa[x]==x ? x : (fa[x]=find(fa[x])) ;
}
void Merge(int x,int y){
int rx=find(x);
int ry=find(y);
if(rx==ry) return;
fa[rx]=ry;
}
void K1(){
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i){
int x=b[i].from;
int y=b[i].to;
int rx=find(x);
int ry=find(y);
if(rx==ry) continue;
fa[rx]=ry;
if(b[i].op==0){
b[i].flag=1;
cnt++;
}
}
}
void K2(){
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i) if(b[i].flag) Merge(b[i].from,b[i].to);
for(int i=1;i<=m;++i){
if(b[i].op) continue;
int x=b[i].from;
int y=b[i].to;
int rx=find(x);
int ry=find(y);
if(rx==ry) continue;
fa[rx]=ry;
b[i].flag=1;
cnt++;
if(cnt==k) break;
}
}
void K3(){
for(int i=1;i<=m;++i){
if(b[i].op==0) continue;
int x=b[i].from;
int y=b[i].to;
int rx=find(x);
int ry=find(y);
if(rx==ry) continue;
fa[rx]=ry;
b[i].flag=1;
}
}
void Solve(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;++i) scanf("%d%d%d",&b[i].from,&b[i].to,&b[i].op);
sort(b+1,b+m+1,cmp1);
K1();
if(cnt>k){
printf("-1\n");
return;
}
int x=find(1);
for(int i=2;i<=n;++i){
if(find(i)!=x) {
printf("-1\n");
return;
}
}
K2();
if(cnt<k){
printf("-1\n");
return;
}
K3();
for(int i=1;i<=m;++i){
if(b[i].flag){
printf("%d %d %d\n",b[i].from,b[i].to,b[i].op);
}
}
}
int main(){
Solve();
return 0;
}
[火星补锅] 水题大战Vol.2 T2 && luogu P3623 [APIO2008]免费道路 题解的更多相关文章
- [火星补锅] 水题大战Vol.2 T1 && luogu P1904 天际线 题解 (线段树)
前言: 当时考场上并没有想出来...后来也是看了题解才明白 解析: 大家(除了我)都知道,奇点和偶点会成对出现,而出现的前提就是建筑的高度突然发生变化.(这个性质挺重要的,我之前没看出来) 所以就可以 ...
- 水题大战Vol.3 B. DP搬运工2
水题大战Vol.3 B. DP搬运工2 题目描述 给你\(n,K\),求有多少个\(1\)到\(n\) 的排列,恰好有\(K\)个数\(i\) 满足\(a_{i-1},a_{i+1}\) 都小于\(a ...
- 金题大战Vol.0 A、凉宫春日的叹息
金题大战Vol.0 A.凉宫春日的叹息 题目描述 给定一个数组,将其所有子区间的和从小到大排序,求第 \(k\) 小的是多少. 输入格式 第一行两个数\(n\),$ k\(,表示数组的长度和\)k$: ...
- 金题大战Vol.0 B、序列
金题大战Vol.0 B.序列 题目描述 给定两个长度为 \(n\) 的序列\(a\), \(b\). 你需要选择一个区间\([l,r]\),使得\(a_l+-+a_r>=0\)且\(b_l+-+ ...
- 金题大战Vol.0 C、树上的等差数列
金题大战Vol.0 C.树上的等差数列 题目描述 给定一棵包含\(N\)个节点的无根树,节点编号\(1-N\).其中每个节点都具有一个权值,第\(i\)个节点的权值是\(A_i\). 小\(Hi\)希 ...
- 土题大战Vol.0 A. 笨小猴 思维好题
土题大战Vol.0 A. 笨小猴 思维好题 题目描述 驴蛋蛋有 \(2n + 1\) 张 \(4\) 星武器卡片,每张卡片上都有两个数字,第 \(i\) 张卡片上的两个数字分别是 \(A_i\) 与 ...
- 火题大战Vol.1 A.
火题大战Vol.1 A. 题目描述 给定两个数\(x\),\(y\),比较\(x^y\) 与\(y!\)的大小. 输入格式 第一行一个整数\(T\)表示数据组数. 接下来\(T\)行,每行两个整数\( ...
- 火题大战Vol.0 B 计数DP
火题大战Vol.0 B 题目描述 \(n\) 个沙茶,被编号 \(1\)~$ n$.排完队之后,每个沙茶希望,自己的相邻的两人只要无一个人的编号和自己的编号相差为 \(1\)(\(+1\) 或\(-1 ...
- [火星补锅] 非确定性有穷状态决策自动机练习题Vol.3 T3 && luogu P4211 [LNOI2014]LCA 题解
前言: 这题感觉还是很有意思.离线思路很奇妙.可能和二次离线有那么一点点相似?当然我不会二次离线我就不云了. 解析: 题目十分清真. 求一段连续区间内的所有点和某个给出的点的Lca的深度和. 首先可以 ...
随机推荐
- ysoserial CommonsColletions1分析
JAVA安全审计 ysoserial CommonsColletions1分析 前言: 在ysoserial工具中,并没有使用TransformedMap的来触发ChainedTransformer链 ...
- sync 修饰符在Vue中如何使用
在有些情况下,我们可能需要对一个 prop 进行"双向绑定".不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源. 这 ...
- 密码学系列之:bcrypt加密算法详解
目录 简介 bcrypt的工作原理 bcrypt算法实现 bcrypt hash的结构 hash的历史 简介 今天要给大家介绍的一种加密算法叫做bcrypt, bcrypt是由Niels Provos ...
- ☕【Java技术指南】「并发编程专题」针对于Guava RateLimiter限流器的入门到精通(含实战开发技巧)
并发编程的三剑客 在开发高并发系统时有三剑客:缓存.降级和限流. 缓存 缓存的目的是提升系统访问速度和增大系统处理容量. 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题 ...
- 手把手教你 Docker Compose安装DOClever
一.什么是Docker Compose以及Docker Compose的安装和使用 查看我的另外一篇博客:Docker Compose的安装和使用 二.DOClever是什么 DOClever是一个可 ...
- 洛谷P1582——倒水(进制,数学)
https://www.luogu.org/problem/show?pid=1582 题目描述 一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水.接着~~CC发现瓶子实在太多了 ...
- 剑指offer计划20( 搜索与回溯算法中等)---java
1.1.题目1 剑指 Offer 07. 重建二叉树 1.2.解法 注释解法. 1.3.代码 class Solution { int[] preorder; HashMap<Integer, ...
- 基于React和GraphQL的黛梦设计与实现
写在前面 这是笔者在中秋无聊写着玩的,假期闲暇之余憋出来的帖子.麻雀虽小,但五脏俱全,涉及到的方方面面还是蛮全的.所以就设计了一个黛梦(demo)------ 打通了GraphQL的接口与前端交互的流 ...
- Shell系列(29)- 单分支if语句格式
单分支if条件语句 if [ 条件判断 ] ;then 程序 fi 或者 if [ 条件判断 ] then 程序 fi 注意点 if语句使用fi结尾,和一般语言使用大括号结尾不同 [ 条件判断 ]就是 ...
- JavaScript进阶面向对象ES6
类和对象 对象:万物皆对象,对象是一个具体的事物,看得见摸得着的实物 对象是由属性和方法组成的: 属性:事物的特征,再对象中用属性来表示(常用名词) 方法:事物的行为,再对象中用方法来表示(常用动词) ...