可持久化并查集
简单来说就是两颗主席树
一颗维护 父亲节点是谁
一颗维护 深度
为什么要维护深度?
降低时间复杂度
并查集如果不优化的查询的话是这样:
int findx(int x)
{
return x == fa[x]?x : findx(fa[x]);
}
void hebing(int x,int y)
{
int xx = findx(x);
int yy = findx(y);
if(xx != yy)
{
fa[xx] = yy;
}
}
这个时间复杂度最坏可以到达O(n) 的
优化一:
路径压缩是这样:
基本是常数复杂度?
void hebing(int x,int y)
{
int xx = findx(x);
int yy = findx(y);
if(xx != yy)
{
fa[xx] = yy;
}
}
int findx(int x)
{
return x == fa[x]?x : fa[x] = findx(fa[x]);
}
但是可持久化的话这样每压缩一次就算一个状态? 可能会MLE?
优化二:
按秩合并:
就是按照他的这个树的大小来合并,把小的树的根节点连到大的树的根节点上面这样可以使树尽量低 查询的复杂度是logn
代码是这样:
int findx(int x)
{
return x == fa[x]?x :findx(fa[x]);
}
void hebing(int x,int y)
{
int xx = findx(x);
int yy = findx(y);
if(xx != yy)
{
int dx = dep[xx];
int dy = dep[yy];
if(dx<dy)//此时树的深度不变
{
fa[xx] = yy;
}
else if(dx> dy)//此时深度也不便
{
fa[yy] = xx;
}
else
{
fa[xx] = yy;
dep[yy]++;//深度加了1
}
}
}
一般写的话应该都写的是那个路径压缩的那个 因为合并查询都很简单而且复杂度还低 但是可持久化的时候可能爆内存。
还有一些别的优化的方法。但是我太菜了不知道
然后可持久化并查集就采用按秩合并
分析一下过程:
查询一个结点的父节点 : 因为在主席树里 所以是 log(n)
查询一个结点的根节点:log(n)*log(n);
合并:查询两个节点当前状态的的根节点,然后查询根节点当前状态的深度,然后按秩合并就好了
可持久化的话只用把fa数组和深度dep数组可持久化就好了 所以是两颗主席树。
两颗主席树都存的是:左节点、右节点、值。
所以用一个结构体,内存开二倍就好了。
两个根数组,一个父亲一个深度
用不用建树?初始化的时候 i 的父节点是 i ,深度都是0;
所以只用建父节点就行了
具体看代码
板子题: 洛谷 P 3402
然后代码:
#include <stdio.h>
#include <algorithm>
using namespace std;
const int maxn = 2e5+5;
struct Node
{
int l,r,num;
}node[maxn*40*2];
int rfa[maxn];
int rdep[maxn];
int cnt;//
int stemp;
//stemp有什么用? 建树的时候记录
void build(int l,int r,int& no)
{
no = ++cnt;
if(l == r)
{
node[no].num = ++stemp;
// printf("%d\n",node[no].num);
return;
}
int mid = l + r >> 1;
build(l,mid,node[no].l);
build(mid+1,r,node[no].r);
}
void update(int l,int r,int per,int &no,int pos,int num)
{
no = ++cnt;
node[no] = node[per];
if(l == r)
{
node[no].num = num;
return;
}
int mid = l + r >> 1;
if(pos <= mid) update(l,mid,node[per].l,node[no].l,pos,num);
else update(mid+1,r,node[per].r,node[no].r,pos,num);
}
int query(int l,int r,int no,int pos)
{
// printf("%d %d\n",no,pos);
if(l == r)
return node[no].num;
int mid = l + r >> 1;
if(pos <= mid) return query(l,mid,node[no].l,pos);
else return query(mid+1,r,node[no].r,pos);
}
int n;
int find(int x,int i)
{
// printf("%d %d\n",x,i);
int xx = query(1,n,rfa[i],x);
if(xx == x) return x;
else return find(xx,i);
}
int main()
{
int m;
scanf("%d%d",&n,&m);
build(1,n,rfa[0]);
// rfa[0] = 1;
for (int i = 1; i <= m; i ++ )
{
int f,x,y;
scanf("%d",&f);
if(f == 1)
{
scanf("%d%d",&x,&y);
int fx = find(x,i-1);
int fy = find(y,i-1);
if(fx == fy)
{
rdep[i] = rdep[i-1];
rfa[i] = rfa[i-1];
continue;
}
x = fx;
y = fy;
int dex = query(1,n,rdep[i-1],x);
int dey = query(1,n,rdep[i-1],y);
if(dex < dey)
{
update(1,n,rfa[i-1],rfa[i],x,y);
rdep[i] = rdep[i-1];
}
else if(dex > dey)
{
update(1,n,rfa[i-1],rfa[i],y,x);
rdep[i] = rdep[i-1];
}
else
{
update(1,n,rfa[i-1],rfa[i],x,y);
update(1,n,rdep[i-1],rdep[i],y,dey+1);
}
}
else if(f == 2)
{
scanf("%d",&x);
rfa[i] = rfa[x];
rdep[i] = rdep[x];
}
else
{
scanf("%d%d",&x,&y);
// printf("111111\n");
int fax = find(x,i-1);
int fay = find(y,i-1);
if(fax == fay)
printf("1\n");
else
printf("0\n");
rfa[i] = rfa[i-1];
rdep[i] = rdep[i-1];
}
// printf("%d %d f\n",i,f);
}
}
看b站习得 大佬链接