启发式合并是针对n个集合(总元素个数是O(n))的合并操作,每次将小的集合合并到大的集合
复杂度证明:
考虑每一个元素的贡献,如果在某一次合并中该元素被移动,那么集合的大小至少是,故复杂度是
具体的题目而言,我们可以看出对于而言,具体是把x变成y,还是把y变成x,其实对结果并不重要,但我们对于每个颜色它的位置的集合需要维护
pos[x]是x颜色(真实的颜色)所在的位置集合
每次将 ,如果将x,y互换(复杂度是O(1)的)
再将pos[x]并到pos[y]上,这样每一个颜色的位置集合都被正确的维护了,至于每个集合pos[x]中的位置存的颜色是否真的是x,对结果并不重要
Code:
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int N = 1e5+10,M=1e6+10; int n, m, k, T; int c[N]; vector<int> pos[M]; int main() { scanf("%d%d",&n,&m); int seg=0; for (int i = 1; i <= n; i++) { scanf("%d", &c[i]); pos[c[i]].push_back(i); } for(int i=1;i<=n;i++) seg+=(c[i]!=c[i-1]); while (m--) { int op; scanf("%d",&op); if (op == 1) { int x,y; scanf("%d%d",&x,&y); if(x==y)continue; if (pos[x].size() > pos[y].size()) { pos[x].swap(pos[y]);//!!!!!! } if(!pos[y].size())continue; int clr=c[pos[y][0]]; for (auto e : pos[x]) { seg+=-(c[e]!=c[e-1])-(c[e]!=c[e+1])+(clr!=c[e-1])+(clr!=c[e+1]); c[e]=clr; pos[y].push_back(e); } pos[x].clear(); } else { printf("%d\n",seg); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下