首页 > 其他分享 >启发式合并

启发式合并

时间:2024-10-08 13:59:45浏览次数:8  
标签:return log Fa int 合并 fa 启发式 集合

入门

例题

[ABC329F] Colored Ball

  • 题意

给定 \(N\) 个盒子,每个盒子里面有一个颜色为 \(C_i\) 的小球。有 \(Q\) 次操作,每次操作将第 \(a_i\) 个盒子中的球都放到第 \(b_i\) 个盒子里面,你需要在每次操作后输出当前操作结束后第 \(b_i\) 个盒子里面有多少个不同颜色的小球。

如果盒子为空,输出 \(0\) 即可。

首先看到对答案有贡献的只有小球的颜色,即种类。因此可以联想到 STL set 实现的自动去重功能。

考虑按题意模拟。若构造一组形如由极小集合合并至极大集合的数据,此算法的最劣复杂度是 \(O(nq\log n)\) 的。

此时考虑启发式合并。

我们考虑让集合中元素个数数量小的合并至大的中。此时可以证明时间复杂度是 \(O(n\log ^2 n)\) 的。

初看上可能感觉这就是个暴力。但是我们分析一下每个元素被 insert 了多少次。

一个集合中的元素被放入另一个集合中会被 insert 一次。但是这个元素所在的集合的大小至少扩大了一倍。所以一个元素最多被 insert \(O(\log n)\) 次。加上 set 本身带有的 \(O(\log n)\) 的复杂度,最终复杂度是 \(O(n\log ^2 n)\) 的。

在这里,对于两个大小不一样的集合,我们将小的集合合并到大的集合中,而不是将大的集合合并到小的集合中。

为什么呢?这个集合的大小可以认为是集合的高度(在正常情况下),而我们将集合高度小的并到高度大的显然有助于我们找到父亲。

让高度小的树成为高度较大的树的子树,这个优化可以做到单次 \(O(\log n)\)。

while(q--) {
		cin >> x >> y;
		if(s[x].size() < s[y].size()) {
			for(auto i : s[x])
				s[y].insert(i);
			s[x].clear();
			cout << s[y].size() << '\n';
		}
		else {
			for(auto i : s[y])
				s[x].insert(i);
			s[y].clear(), swap(s[x], s[y]);
			cout << s[y].size() << '\n';
		}
	}

P3201 [HNOI2009] 梦幻布丁

dsu on tree

例题

[ABC350G] Mediator

首先一个点是否与询问点对相邻,可以转换为对父亲的讨论。

下列情况是有解的:

  • \(fa_u = fa_v\) 且 \(u, v\) 不是根节点,答案为 \(fa_u\)
  • \(fa_{fa_u} = v\),答案为 \(fa_u\)
  • \(fa_{fa_v} = u\),答案为 \(fa_v\)

画图分析较为移动。

于是我们只需要维护父亲即可,考虑启发式合并。

每次将连通块大小较小的合并至较大的连通块内,对于每次这种操作暴力 dfs 修改 \(fa\) 即可。

连通块大小可以用并查集轻松维护。

证明复杂度:每次连通块大小最多扩大一倍,所以复杂度 \(O(n\log n)\)。

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;

const int N = 1e5 + 5;
const int mod = 998244353;
int n, Q, u, v, op, ans, fa[N];
vector<int> g[N];

inline void dfs(int x, int last) {
	fa[x] = last;
	
	for(auto u : g[x])
		if(u != last) dfs(u, x);
	
	return ;
}

namespace USF {
	int Fa[N], sz[N];
	
	inline void init() {
		for(int i = 1 ; i < N ; ++ i)
			Fa[i] = i, sz[i] = 1;
		
		return ;
	}
	
	inline int find(int x) {
		if(x != Fa[x]) Fa[x] = find(Fa[x]);
		
		return Fa[x];
	}
	
	inline void merge(int x, int y) {
		int fx = find(x), fy = find(y);
		
		if(fx == fy) return ;
		
		if(sz[fx] < sz[fy]) swap(x, y), swap(fx, fy);
		
		dfs(y, x);
		
		sz[fx] += sz[fy], Fa[fy] = fx;
		
		g[x].pb(y), g[y].pb(x);
		
		return ;
	}
}

using namespace USF;

inline int query(int u, int v) {
	if(fa[u] == fa[v] && fa[u]) return fa[u];
	if(fa[fa[u]] == v) return fa[u];
	if(fa[fa[v]] == u) return fa[v];
	
	return 0;
}

signed main() {
	ios_base :: sync_with_stdio(NULL);
	cin.tie(nullptr);
	cout.tie(nullptr);
	
	cin >> n >> Q;
	
	init();
	
	while(Q --) {
		cin >> op >> u >> v;
		
		op = 1 + ((op * (1 + ans)) % mod) % 2;
		u = 1 + ((u * (1 + ans)) % mod) % n;
		v = 1 + ((v * (1 + ans)) % mod) % n;
		
		if(op == 1) merge(u, v);
		else {
			ans = query(u, v);
			
			cout << ans << '\n';
		}
	}
	
	return 0;
}

标签:return,log,Fa,int,合并,fa,启发式,集合
From: https://www.cnblogs.com/endswitch/p/18451514

相关文章

  • table 单元格合并
    table元素合并单元格,用法倒是很简单,但过程中遇到了点小问题,记录下:1、多行多列合并,使用 rowSpan、colSpan设置要合并的行列数,再将合并后的多余单元格删除即可:functionmerge(table,px,py,row,col,remove=true){py--;lettarget=table.rows[px].......
  • 代码随想录算法训练营 | 56. 合并区间,738.单调递增的数字
    56.合并区间题目链接:56.合并区间文档讲解︰代码随想录(programmercarl.com)视频讲解︰合并区间日期:2024-10-06想法:重叠区间类似问题Java代码如下:classSolution{publicint[][]merge(int[][]intervals){List<int[]>res=newArrayList<>();Arra......
  • 怎样把两个视频合并成一个视频?批量合并视频看好这6个工具!
    ★ 怎样把两个视频合并成一个视频?随着视频内容的日益丰富,我们常常需要将多个视频片段合并成一个完整的视频文件,不管将2个视频合并拼接到一个进行播放,还是直接合并2个短视频变成长视频,都可以通过这6个工具进行处理。无论你是视频编辑新手还是经验丰富的创作者,选择合适的工具......
  • 代码随想录算法训练营Day17 | 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜
    目录654.最大二叉树617.合并二叉树700.二叉搜索树中的搜索98.验证二叉搜索树654.最大二叉树题目654.最大二叉树-力扣(LeetCode)给定一个不重复的整数数组nums。最大二叉树可以用下面的算法从nums递归地构建:创建一个根节点,其值为nums中的最大值。递归地在......
  • 【办公类-48-03】20240930每月电子屏台账汇总成docx-3(三园区合并EXCLE,批量生成3份word
    背景需求:前期电子屏汇总是“总园”用“”问卷星”、“一分园”用“腾讯文档”,二分园“用“手写word””【办公类-48-02】20240407每月电子屏台账汇总成docx-2(腾讯文档xlsx导入docx,每页20条)【办公类-48-02】20240407每月电子屏台账汇总成docx-2(腾讯文档xlsx导入docx,每页20......
  • 【LeetCode Hot 100】23. 合并K个升序链表
    题目描述看到这个题目会想起之前做过的合并2个升序链表。在那个题目中,由于两个链表都已经是升序的,可以将两个链表的元素进行逐个比较并添加到答案链表中。但是在本题中,每次循环都需要在k个链表的当前元素中找出最小的,而且需要在所有k个链表都遍历完之后跳出循环,所以效率比较低。......
  • EasyExcel导出合并单元格
    处理结果:把a,b列相同内容的单元格进行合并引入easyexcel:<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.1</version></dependency>示例代码:publicvoidexportStrategyDetail(......
  • aspose.cell 合并内容一样的单元格
    ///<summary>///数据合并///</summary>///<paramname="mySheet"></param>///<paramname="columnIndex">合并的列下标</param>///<paramname="dataIndex">从哪一行开始</param>///<paramna......
  • 大规异构集群 混合并行分布式训练系统,解决算力不均衡问题 HETHUB
    视频教程在这:3.2大规模异构集群,混合并行分布式系统,解释算力不均衡问题HETHUB_哔哩哔哩_bilibili一、大规模异构集群出现的原因:同一种GPU数量有限难以构建大规模集群:训练大规模模型依赖于大量的计算资源。例如,训练GPT-4模型(1.8万亿个参数)需要25000个A100GPU。用一种GPU加速......
  • 【hot100-java】【合并两个有序链表】
    记忆中,两个指针合并即可。 建立哨兵节点dum/***Definitionforsingly-linkedlist.*publicclassListNode{*intval;*ListNodenext;*ListNode(){}*ListNode(intval){this.val=val;}*ListNode(intval,ListNodenext......