首页 > 其他分享 >并查集扩展应用

并查集扩展应用

时间:2024-07-09 09:01:23浏览次数:20  
标签:int namespace 查集 扩展 long fa 应用 using 战舰

并查集扩展应用

A. 货物运输

题目描述

有 \(n\) 座城市和 \(m\) 条双向道路。已知走过每条边所需要的汽油量,\(q\) 次询问,求汽油量为 \(l\) 的车可以在多少对城市之间运送货物。(汽车到达城市会立刻把油全部加满)

题解

这道题没有强制在线,所以可以考虑进行离线。

对于大小为 \(n\) 一个连通块,两两相连的点对一共有 \(\frac{n(n-1)}{2}\)
于是 很难不想到 使用并查集维护连通块的大小,动态更新答案。

# include <bits/stdc++.h> 
using namespace std; 
using namespace IOS; 
typedef long long ll; 
# define int long long 
# define lc u << 1
# define rc u << 1 | 1 
const int N = 100005; 

int n, m, q; 
struct edge 
{
    int u, v, w; 
    bool operator < (const edge &t) const { return w < t.w; }
} e[N]; 
struct query
{
    int l, id; 
    bool operator < (const query &t) const { return l < t.l; }
} Q[N]; 
int fa[N], sz[N]; 
int find_fa (int u) { return u == fa[u] ? u : fa[u] = find_fa (fa[u]); }
int ans; 
void union_fa (int u, int v)
{
    u = find_fa (u), v = find_fa (v); 
    if (u != v)
    {
        fa[v] = u; 
        ans -= sz[u] * (sz[u] - 1) + sz[v] * (sz[v] - 1); 
        sz[u] += sz[v]; 
        ans += sz[u] * (sz[u] - 1); 
	}
}
int res[N]; 
signed main ()
{
    read (n, m, q); 
    for (int i = 1; i <= m; i ++ ) read (e[i].u, e[i].v, e[i].w); 
    sort (e + 1, e + 1 + m); 
    for (int i = 1; i <= q; i ++ )
    {
        read (Q[i].l); 
        Q[i].id = i; 
    }
    sort (Q + 1, Q + 1 + q); 
	for (int i = 1; i <= n; i ++ ) fa[i] = i, sz[i] = 1; 
    for (int i = 1, j = 1; i <= q; i ++ )
    {
        while (j <= m && e[j].w <= Q[i].l) union_fa (e[j].u, e[j].v), j ++ ; 
        res[Q[i].id] = ans; 
	}
    for (int i = 1; i <= q; i ++ ) write (res[i] / 2, '\n'); 
	return 0;
}

B. 银河英雄传说

题目描述

yyc 在一次战斗中,他将 1233 战场划分成 \(30000\) 列,每列依次编号为 \(1, 2,\ldots ,30000\)。之后,他把自己的战舰也依次编号为 \(1, 2, \ldots , 30000\),让第 \(i\) 号战舰处于第 \(i\) 列。这是初始阵形。当进犯之敌到达时,yyc 会多次发布合并指令。合并指令为 M i j,含义为第 \(i\) 号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第 \(j\) 号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。

然而,cdx 可以通过庞大的情报网络随时监听 yyc 的舰队调动指令。

在 yyc 发布指令调动舰队的同时,cdx 为了及时了解当前 yyc 的战舰分布情况,也会发出一些询问指令:C i j。该指令意思是,询问电脑,yyc 的第 \(i\) 号战舰与第 \(j\) 号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。

题解

\(fa_i\) 表示飞船 \(i\) 所在列的队头。
\(d_i\) 表示飞船 \(i\) 到其所在队列队头的距离,则飞船 \(i\)和飞船 \(j\) 之间的飞船数量 \(|d_i-d_j|-1\)。
\(sz_i\) 表示飞船 \(i\) 所在的队列的大小

使用并查集维护即可。

# include <bits/stdc++.h> 
using namespace std; 
using namespace IOS; 
typedef long long ll; 
//# define int long long 
# define lc u << 1
# define rc u << 1 | 1 
const int N = 30005; 

int m; 
int fa[N], sz[N], d[N]; 
int find_fa (int u)
{
    if (u == fa[u]) return fa[u]; 
    int v = find_fa (fa[u]); 
    d[u] += d[fa[u]]; 
    fa[u] = v; 
    return v; 
}
void union_fa (int u, int v)
{
	u = find_fa (u), v = find_fa (v);
	if (u != v) fa[u] = v, d[u] = sz[v], sz[v] += sz[u];
}
signed main ()
{
	for (int i = 1; i < N; i ++ ) fa[i] = i, sz[i] = 1;
	read (m); 
	while (m -- )
	{
		char op; int u, v; read (op, u, v); 
		if (op == 'M') union_fa (u, v);
		else
		{
			int fu = find_fa (u), fv = find_fa (v);
            if (fu != fv) write (-1, '\n'); 
            else write (max (0, abs (d[u] - d[v]) - 1), '\n');
		}
	}
	return 0;
}

C. 罗马游戏

题目描述

监狱里面有 \(n\) 个人,每个人都是一个独立的团。最近统计了每个人善良程度。狱卒可以发两种命令:

  1. Merge(i, j)。把 \(i\) 所在的团和j所在的团合并成一个团。如果 \(i, j\) 有一个人是死人,那么就忽略该命令。
  2. Kill(i)。把 \(i\)所在的团里面善良程度最低的人杀死。如果 \(i\) 这个人已经死了,这条命令就忽略。 狱卒希望他每发布一条 kill 命令,下面的刀斧手就把被杀的人的善良程度报上来。(如果这条命令被忽略,那么就报 \(0\))

题解

启发式合并+并查集

# include <bits/stdc++.h> 
using namespace std; 
using namespace IOS; 
typedef long long ll; 
//# define int long long 
# define lc u << 1
# define rc u << 1 | 1 
const int N = 1000005; 

int n, q; 
bool dead[N]; 
int fa[N], sz[N]; 
priority_queue <int, vector <int>, greater <int> > grp[N]; 
int find_fa (int u) { return u == fa[u] ? u : fa[u] = find_fa (fa[u]); }
void union_fa (int u, int v)
{
    u = find_fa (u), v = find_fa (v); 
    if (u != v)
    {
        if (sz[u] < sz[v]) swap (u, v); 
        fa[v] = u; 
        while (!grp[v].empty ()) grp[u].push (grp[v].top ()), grp[v].pop (); 
        sz[u] += sz[v]; 
    }
}
map <int, int> mp; 
signed main ()
{
    read (n); 
    for (int i = 1; i <= n; i ++ ) fa[i] = i, sz[i] = 1; 
    for (int i = 1; i <= n; i ++ )
    {
        int x; read (x); 
        grp[i].push (x); 
        mp[x] = i; 
    }
    read (q); 
    while (q -- )
    {
        char c; int x, y; read (c, x); 
        if (c == 'M')
        {
            read (y); 
            if (dead[x] || dead[y]) continue; 
            union_fa (x, y); 
        }
        else
        {
            if (dead[x]) { write (0, '\n'); continue; }
            x = find_fa (x); 
            int u = grp[x].top (); grp[x].pop (); 
            dead[mp[u]] = 1; 
            write (u, '\n'); 
        }
    }
    return 0; 
}

D. 矩阵

题目描述

有一个 \(n\times m\)的矩阵,初始每个格子的权值都为 \(0\),可以对矩阵执行两种操作:

  1. 选择一行,该行每个格子的权值加 \(1\) 或减 \(1\)。
  2. 选择一列,该列每个格子的权值加 \(1\) 或减\(1\)。

现在有 \(K\) 个限制,每个限制为一个三元组 \((x,y,c)\),代表格子 \((x,y)\) 权值等于\(c\)。
问是否存在一个操作序列,使得操作完后的矩阵满足所有的限制。
如果存在输出 Yes,否则输出 No

题解

定义横着是加,竖着是减。那么每个限制可以转化为:\(A_x-A_y=c\)(\(A\) 增加量或减少量)
带权并查集维护的是到根节点的距离,那么这个也可以类比到根节点的距离。
\(A_x-A_y=c\) 转化为 \(A_x=A_y+c\)。
那么每次修改的其实就是到根节点的距离。

# include <bits/stdc++.h> 
using namespace std; 
using namespace IOS; 
typedef long long ll; 
//# define int long long 
# define lc u << 1
# define rc u << 1 | 1 
const int N = 2005; 

int n, m, q; 
int fa[N], d[N]; 
int find_fa (int u)
{
    if (u == fa[u]) return u; 
    int v = find_fa (fa[u]); 
    d[u] += d[fa[u]]; 
    return fa[u] = v; 
}
signed main ()
{
    int T; read (T); 
    while (T -- )
    {
        read (n, m, q); 
        for (int i = 1; i <= n + m; i ++ ) fa[i] = i, d[i] = 0; 
        int ans = 1; 
        while (q -- )
        {
            int u, v, w; read (u, v, w); 
            v += n; 
            int fu = find_fa (u), fv = find_fa (v); 
            if (fu != fv)
            {
                fa[fu] = fv; 
                d[fu] = d[v] - d[u] + w; 
            }
            else if (d[u] - d[v] != w) ans = 0; 
		}
		if (ans) puts("Yes");
		else puts("No");
	}
	return 0;
}

标签:int,namespace,查集,扩展,long,fa,应用,using,战舰
From: https://www.cnblogs.com/legendcn/p/18288394

相关文章

  • 【深度学习】探讨最新的深度学习算法、模型创新以及在图像识别、自然语言处理等领域的
    深度学习作为人工智能领域的重要分支,近年来在算法、模型以及应用领域都取得了显著的进展。以下将探讨最新的深度学习算法与模型创新,以及它们在图像识别、自然语言处理(NLP)等领域的应用进展。一、深度学习算法与模型创新新型神经网络结构Transformer及其变种:近年来,Transformer......
  • 扩展欧几里得详解——同余方程
    对于同余方程的话就是一个经典扩展欧几里得求逆元的题目。这个可以转换成,我们需要求的只是x和k从而得到一组解。通常我们会得到a和b两个元素,假设a是7,b为40,通过扩展欧几里得进行运算。这时也就是,我们第一步先开始从a,b两个数字里找到最大的那个在这里的话是40,然后利用大的......
  • 关于Python中的series详解与应用
    引言近期在学习Python的过程中学到了Pandas库,它是数据处理操作中一款非常强大且流行的工具。而Pandas的两个核心数据结构是Series和DataFrame(下一篇文章便会进行有关学习)。本篇将详细介绍Series,主要包括它的定义、创建方法、常用操作、应用场景以及与其他数据结构的比较,仅为......
  • 【js面试题】深入理解尾递归及其在JavaScript中的应用
    面试题:举例说明尾递归的理解,以及应用场景引言:在编程中,递归是一种常见的解决问题的方法,它允许函数调用自身来解决问题。然而,递归如果不当使用,可能会导致栈溢出错误,特别是在处理大量数据时。尾递归是一种特殊的递归形式,它能够优化递归调用,避免栈溢出的问题。本文将深入探......
  • 单片机知多少之STM32F103-GPIO输出应用篇
    示例:选择GPIOB做流水灯控制逻辑将8个发光二极管的负端分别接入PB0~PB7,正端接5V电源,当配置GPIO为低电平时,回路导通,二极管开始工作,亮灯;当配置GPIO为高电平时,回路等电位断开,二极管不工作,灭灯,使GPIO输出按一定顺序执行,即流水灯。编写代码变量定义:GPIO_InitTypeDefGPIO_InitSt......
  • 【汽车故障诊断4】一文了解故障诊断码DTC验证程度、快照和扩展数据
    在上篇【汽车故障诊断3】一文了解诊断故障码DTC状态位介绍DTC状态位,本篇文章将继续介绍DTC的其他信息:DTC严重程度,DTC快照和DTC扩展数据。1什么是DTC严重程度  DTC严重程度占用1个字节数据,包含两部分信息,DTC严重程度信息(3位)和DTC类别信息(5位),如下所示:source:ISO15031-6 ......
  • 深度学习全景进阶:最新Python深度学习进阶与前沿应用
    近年来,伴随着以卷积神经网络(CNN)为代表的深度学习的快速发展,人工智能迈入了第三次发展浪潮,AI技术在各个领域中的应用越来越广泛。系统掌握AI新理论、新方法及其Python代码实现。注意力机制、Transformer模型(BERT、GPT-1/2/3/3.5/4、DETR、ViT、SwinTransformer等)、生成式模型(变......
  • Altair携手奇瑞汽车,荣获2024世界人工智能大会“AI赋能新型工业化创新应用优秀案例”
    2024年7月4-7日,2024世界人工智能大会(WAIC)在上海世博中心成功举办。4日下午,“AI赋工业,数智启未来—人工智能赋能新型工业化主题论坛”在上海世博中心召开。Altair携手奇瑞汽车股份有限公司申报的“基于AI的降阶建模实现新能源汽车高低温续航高效集成仿真”案例在本次大会中......
  • 推出支持第五代CAPSENSE™技术的PSoC™ 车规级4100S Max系列(CY8C4147AZS、CY8C4148AZA
    全新的PSoC™4100SMax系列产品带有扩展的闪存器件与通用输入/输出接口(GPIO),支持第五代CAPSENSE™电容和电感式触摸感应技术,能够满足新一代人机交互(HMI)应用的需求。PSoC4100SMax采用CAPSENSE™技术,拥有7x7mm²、10x10mm²和14x14mm²三种封装尺寸,是工业控制、汽车人机交互(HMI......
  • 大语言模型的应用探索—AI Agent初探!
    前言大语言模型的应用之一是与大语言模型进行聊天也就是一个ChatBot,这个应用已经很广泛了。接下来的一个应用就是AIAgent。AIAgent是人工智能代理(ArtificialIntelligenceAgent)的概念,它是一种能够感知环境、进行决策和执行动作的智能实体,通常基于机器学习和人工智能技术,具备......