codeforces 562D 566D Restructuring Company
两个并查集。
一个并查集用于维护 部门合并
另一个用于维护第二种操作的线段合并。 线段附加一个属性,为该线段最右端的位置。
两个并查集均能采用 按秩优化 与路径压缩。
从而复杂度为 接近线性。 O((n+q)(alpha(n)));
#include <cstdio>
#define maxsize 200002
int myrank0[maxsize], father0[maxsize], father1[maxsize], mynext[maxsize], myrank1[maxsize];
int n, q;
inline int LINK0(int id1, int id2)
{
int rank1, rank2;
if ((rank1 = myrank0[id1]) > (rank2 = myrank0[id2]))
{
return father0[id2] = id1;
}
else
{
if (rank1 == rank2)
{
++myrank0[id2];
}
return father0[id1] = id2;
}
}
inline int LINK1(int id1, int id2)
{
int rank1, rank2;
if ((rank1 = myrank1[id1]) > (rank2 = myrank1[id2]))
{
return father1[id2] = id1;
}
else
{
if (rank1 == rank2)
{
++myrank1[id2];
}
return father1[id1] = id2;
}
}
inline int findfather0(int index)
{
if (index != father0[index])
{
father0[index] = findfather0(father0[index]);
}
return father0[index];
}
inline int findfather1(int index)
{
if (index != father1[index])
{
father1[index] = findfather1(father1[index]);
}
return father1[index];
}
inline void UNION0(int id1, int id2)
{
LINK0(findfather0(id1), findfather0(id2));
}
inline void UNION1(int id1, int id2)
{
LINK1(findfather1(id1), findfather1(id2));
}
void init()
{
for (int i = 1; i <= n; ++i)
{
mynext[i] = (father0[i] = father1[i] = i) + 1;
}
}
void getInput()
{
scanf("%d%d", &n, &q);
init();
int flag, x, y, x1, x2;
for (int i = 0; i < q; ++i)
{
scanf("%d%d%d", &flag, &x, &y);
if (1 == flag)
{
UNION0(x, y);
}
else if (2 == flag)
{
++y;
x1 = mynext[findfather1(x)];
while (x1 < y)
{
UNION0(x, x1);
x2 = mynext[findfather1(x1)];
UNION1(father1[x], father1[x1]);
x = x1;
x1 = x2;
}
mynext[findfather1(father1[x])] = x1;
}
else
{
if (findfather0(x) == findfather0(y))
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
}
}
int main()
{
getInput();
return 0;
}
复杂度分析:
第一种:复杂度就是正常的并查集复杂度。
第二种: 对于x-y进行合并。 注意到,每次进行第二种查询时,都至少有一个findfather操作,后面再紧跟着ki个 区间合并与findfather操作。而每进行一次,区间数就少1。总区间数只有n。因此q次查询至多进行q+n次findfather操作与n次区间合并操作。
因此总复杂度仍然是 并差集 的复杂度,几乎线性。