推荐
如何解决两个区间的并查集
例题。
首先考虑暴力怎么写,对于每一次 操作,直接暴力加边,时间复杂度为 。
瓶颈在于 操作,考虑如何优化。
对于一次 操作,可以看成若干次 操作的结合体,具体可以看下面两张图:

红色的是原始序列,蓝色和紫色分别是可以被分割的区域,而灰色不行,因为红色序列中有一段区间没有被包含到。
分成两个区间比较方便,接下来考虑如何分成两个区间。
那分完这两个区间之后考虑这两个区间有什么性质比较好维护。区间长度是 的整数次方是一个比较好的性质,可以很自然的想到 ST 表。
在下文中 表示为满足 的区间 。
接下来区间长度已经是 的整数次幂了,就可以对于每一次要进行操作的区间 ,就可以每次分成 和 进行操作。
这么进行下来,操作 的时间复杂度就成功降到了 了。
代码:
#include<bits/stdc++.h>
const int MAXN = 2e5 + 5, MAXM = 30;
int n, q;
int lg[MAXN], fa[MAXN][MAXM], mi[MAXN], mx[MAXN];
inline void init()
{
lg[0] = -1;
for(int i = 1; i <= n; i++)
lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= n; i++)
fa[i][0] = i;
for(int j = 1; j <= lg[n]; j++)
for(int i = 1; i <= n; i++)
fa[i][j] = i;
for(int i = 1; i <= n; i++)
mi[i] = mx[i] = i;
}
inline int getf(int x, int s)
{
if(fa[x][s] == x) return x;
return fa[x][s] = getf(fa[x][s], s);
}
inline void modify(int l, int r, int s)
{
int xf = getf(l, s), yf = getf(r, s);
if(xf == yf) return;
fa[xf][s] = yf;
if(!s)
{
mi[yf] = std::min(mi[yf], mi[xf]);
mx[yf] = std::max(mx[yf], mx[xf]);
return;
}
modify(l, r, s - 1), modify(l + (1 << (s - 1)), r + (1 << (s - 1)), s - 1);
}
int main()
{
scanf("%d%d", &n, &q);
init();
for(int _ = 1, op, x, l, r, len; _ <= q; _++)
{
scanf("%d", &op);
if(op == 1) scanf("%d", &x);
else scanf("%d%d%d", &l, &r, &len);
if(op == 1)
printf("%d %d\n", mi[getf(x, 0)], mx[getf(x, 0)]);
else
{
int s = lg[len];
modify(l, r, s);
modify(l + len - (1 << s), r + len - (1 << s), s);
}
} return 0;
}
所以当两个区间进行操作时,可以考虑用像 ST 表这类的东西优化复杂度。
京公网安备11010802045784号
前排