题解 CF566D【Restructuring Company】
区间合并的并查集。
区间合并操作显然不能硬上,考虑如何优化这一段的合并。
例如,假设目前并查集长这样(如下图),此时希望合并 ,你会怎么做呢?
相信我们的第一反应都是:直接把左侧三个集合合并就好了,而不是去合并 。
这确实就是我们的做法,那么我们就需要知道每个集合管辖的范围是哪段区间,或者说需要知道在这一集合后方不属于这一集合的第一个点是多少。我们维护一个数组 表示以 为根的那个集合后面不属于这一集合的第一个点,例如 。
然后我们在区间内找到每个集合的第一个点并与前一个集合合并即可。时间复杂度易证(共合并不超过 次)。
//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n, m, nxt[N];
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
struct Dsu {
int fa[N];
void init(int x) {rep(i, 1, x) fa[i] = i;}
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
bool merge(int x, int y) {
int u = find(x), v = find(y);
if(u == v) return 0;
fa[v] = u;
return 1;
}
}dsu;
int main() {
scanf("%d%d", &n, &m);
rep(i, 1, n) nxt[i] = i + 1;
dsu.init(n);
rep(i, 1, m) {
int op, x, y;
scanf("%d%d%d", &op, &x, &y);
if(op == 1) dsu.merge(x, y);
else if(op == 2) {
int lst = 0;
for(int i=x+1;i<=y;i=nxt[i]) {
dsu.merge(i-1, i);
if(lst) nxt[lst] = nxt[y];
lst = i;
}
nxt[lst] = nxt[y];
}
else puts(dsu.find(x)==dsu.find(y)?"YES":"NO");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现