第十四届蓝桥杯备赛模板题——蓝桥部队 (带权并查集)

2023-12-19 01:40

本文主要是介绍第十四届蓝桥杯备赛模板题——蓝桥部队 (带权并查集),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1.蓝桥部队
    • 1. 问题描述
    • 2.输入格式
    • 3.输入样例
    • 4.样例答案
    • 5.原题连接
  • 2.解题思路
  • 3.Ac_code

1.蓝桥部队

1. 问题描述

小明是蓝桥部队的长官,他的班上有 N N N 名军人和 1 1 1 名军师。

这天, N N N 名军人在操场上站成一排,起初编号为 i i i 的军人站在第 i i i 列。

作为长官,小明可以对军人和军师下达 M M M 条命令,命令有两种类型,格式如下:

  • 1 x y,让军人 x x x 所在列的所有人作为一个整体移动到和军人 y y y 所在列的后面,使两列合并为一列。
  • 2 x y,询问军师军人 x x x 和军人 y y y 是否站在同一列。若是,则军师要回应小明 x , y x,y x,y 之间有多少人,否则军师要回应 − 1 -1 1
    你就是小明的军师,请你回答小明的每个询问。

2.输入格式

输入第 1 1 1 行包含两个正整数 N , M N,M N,M,分别表示军人的数量和小明的命令条数。
2 ∼ M + 1 2∼M+1 2M+1 行每行表示一条命令。
1 ≤ N ≤ 1 0 5 , 1 ≤ M ≤ 3 × 1 0 5 , 1 ≤ x , y ≤ N 。 1 \leq N \leq 10 ^5,1≤M≤3×10^5,1≤x,y≤N。 1N105,1M3×1051x,yN

3.输入样例

3 5
2 1 2
1 2 1
2 1 2
1 1 3
2 2 3

4.样例答案

-1
0
1

5.原题连接

蓝桥部队

2.解题思路

这是一道带权并查集的模板题,将 x x x y y y 合并到同一列和判断 x x x y y y 是否在同一个队列中,我们可以使用朴素的并查集维护。但是题意还要求我们求出在同一列中 x x x y y y 相距多远,这就是需要我们在使用并查集的同时维护额外的信息来帮助我们解答。使用使用数组d[]来维护每个点到其根节点的距离。如图:
在这里插入图片描述
1为根节点,可知 d [ 1 ] = 0 d[1]=0 d[1]=0 d [ 2 ] = 1 d[2]=1 d[2]=1 d [ 3 ] = 2 d[3]=2 d[3]=2 d [ 4 ] = 3 d[4]=3 d[4]=3。当询问同一列里 x x x b b b 之间相差多少人 s s s 时,可以得到计算公式:
s = a b s ( d [ x ] − d [ y ] ) − 1 s=abs(d[x]-d[y])-1 s=abs(d[x]d[y])1
最开始每个点都是自己的根节点,所以d[]的初始化值全为 0 0 0
首先考虑并查集核心操作之一的合并操作:
在这里插入图片描述
当我们将队列1接到队列5时,此时队列5中的点d[]的值无需改变,需要修改的队列1,首先考虑 d [ 1 ] d[1] d[1] 会如何变化?因为1本身是根节点,所以 d [ x ] d[x] d[x]的值为0,但当它接到队列5时,它就不是根节点了,那么 d [ 1 ] d[1] d[1]的值应该更新为它到新祖宗的距离,也就是到节点5的距离,假设 s i z e [ x ] size[x] size[x] 为点 x x x 所在列的元素个数,那么很明显 d [ 1 ] d[1] d[1]的值应该更新为 s i z e [ 5 ] size[5] size[5],也就是它即将接上的列的元素个数。
那么队列1中其余元素的d[]值应该如何更改呢?此时 d [ 2 ] 、 d [ 2 ] 、 d [ 3 ] d[2]、d[2]、d[3] d[2]d[2]d[3]表示的还都是指向1的距离,我们应该将其更新为到5的距离,那么只需要加上15的距离即可。也就是到新祖宗节点的距离等于到老祖宗节点的距离加上新祖宗节点到老祖宗节点的距离。
在合并的时候考虑到效率问题,我们只需要先修改祖宗节点即可,其余的值在路径压缩,也就是在查询祖宗节点时在更新。比如上图合并时我们主要将 d [ 1 ] d[1] d[1]的值修改即可,对于 234 234 234 我们先无需修改,再调用 f i n d ( ) find() find() 函数查询祖宗时再同时更新。
在这里插入图片描述
当路径压缩成功时,图将如上所示,指向根节点的值就是到根节点距离。详细的操作见代码讲解。

3.Ac_code

Java

import java.io.*;
import java.util.Arrays;public class Main {static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));public static void main(String[] args) throws IOException {String[] s=br.readLine().split(" ");int n=Integer.parseInt(s[0]);int m=Integer.parseInt(s[1]);UF uf=new UF(n+10);for (int i = 0; i < m; i++) {s=br.readLine().split(" ");int op=Integer.parseInt(s[0]);int x=Integer.parseInt(s[1]);int y=Integer.parseInt(s[2]);if (op==1){uf.merge(y,x);}else{out.println(uf.dist(x,y));}}out.flush();}
}
//带权并查集模板,可复制使用
class UF {int[] f, d, siz;//构造函数初始化并查集public UF(int n) {f = new int[n];d = new int[n];siz = new int[n];for (int i = 1; i < n; ++i) {f[i] = i;}Arrays.fill(siz, 1);}//核心操作,再查询的时候同时更新d[x]的值int find(int x) {if (x == f[x]) return f[x];//先记录祖宗int root = find(f[x]);//加上父亲的距离d[x] += d[f[x]];//指向祖宗return f[x] = root;}boolean same(int x, int y) {return find(x) == find(y);}//让y列接在x列的后面。boolean merge(int x, int y) {x = find(x);y = find(y);if (x == y) return false;//本来d[y]等于0,现在它指向x的距离就是x的元素个数d[y] += siz[x];//此时x列的个数变多siz[x] += siz[y];f[y] = x;return true;}int size(int x) {return siz[find(x)];}//查询两点之间相差几个人,不在一列返回-1int dist(int x, int y) {if (!same(x, y)) return -1;return Math.abs(d[x] - d[y]) - 1;}
}

C++

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> PII;
#define pb(s) push_back(s);
#define SZ(s) (int)s.size();
#define ms(s,x) memset(s, x, sizeof(s))
#define all(s) s.begin(),s.end()
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 200010;struct UF {std::vector<int> f, d, siz;UF(int n) : f(n), d(n, 0), siz(n, 1) { std::iota(f.begin(), f.end(), 0); }int find(int x) {if (x == f[x]) return f[x];//先记录祖宗int root = find(f[x]);//加上父亲的距离d[x] += d[f[x]];//指向祖宗return f[x] = root;}bool same(int x, int y) { return find(x) == find(y); }bool merge(int x, int y) {x = find(x);y = find(y);if (x == y) return false;d[y] += siz[x];siz[x] += siz[y];f[y] = x;return true;}int size(int x) { return siz[find(x)]; }int dist(int x, int y) {if (!same(x, y)) return -1;return abs(d[x] - d[y])-1;}
};
int n, m, op, x, y;
void solve()
{cin >> n>>m;UF uf(n + 10);for (int i = 1; i <= m; ++i){cin >> op >> x >> y;if (op == 1){uf.merge(y, x);} else {cout<<uf.dist(x,y)<<'\n';}}
}
int main()
{ios_base :: sync_with_stdio(false);cin.tie(nullptr);int t=1;while (t--){solve();}return 0;
}

这篇关于第十四届蓝桥杯备赛模板题——蓝桥部队 (带权并查集)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/510487

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

uva 1342 欧拉定理(计算几何模板)

题意: 给几个点,把这几个点用直线连起来,求这些直线把平面分成了几个。 解析: 欧拉定理: 顶点数 + 面数 - 边数= 2。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#inc

uva 11178 计算集合模板题

题意: 求三角形行三个角三等分点射线交出的内三角形坐标。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vector>#include <

poj 1182 并查集 食物链类

题意: 有n只动物,分别编号1....n。所有动物都属于A,B,C中的一种,已知A吃B,B吃C,C吃A。 按顺序给出下面两种共K条信息: 1. x 和 y 属于同一类。 2. x 吃 y 。 然而这些信息可能会出错,有可能有的信息和之前给出的信息矛盾,也有的信息可能给出的 x 和 y 不在n的范围内。 求k条信息中有多少条是不正确的。 解析: 对于每只动物,创建3个元素 i

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

最大流、 最小费用最大流终极版模板

最大流  const int inf = 1000000000 ;const int maxn = 20000 , maxm = 500000 ;struct Edge{int v , f ,next ;Edge(){}Edge(int _v , int _f , int _next):v(_v) ,f(_f),next(_next){}};int sourse , mee

POJ1988带权并查集

M u v 将u所在的堆移动到v所在的堆的上面 C u 求u的下面有多少块 带权并查集 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;i

POJ1703带权并查集

D: u v  u与v在不同的集合 A: u v  查询u与v的关系 1)压缩路径过程        fu->root   0  1  u-fu 0                 0  1   1                 1  0 2)合并过程 fu->fv      u->fu   0 1   v->fv 0            1 0 1            0