可撤销并查集是支持后悔操作的并查集,注意这时写并查集一定要按秩合并,路径压缩会改变节点与节点之间的关系,改了这个关系那就没法回退了。
思路:用一个栈维护每次操作更新的节点,回退时找到那两个回退即可。
注意一定是后操作的先反悔。
int parent[maxn],siz[maxn]; //按秩合并用siz,siz小的连向siz大的
vector<pii> tmp; //记录上次合并用到的节点
int find(int p) //递归找根节点,注意没有路径压缩
{
if( p == parent[p] ) return p;
return find(parent[p]);
}
void done(pii v) //合并两个点
{
int x = find(v.fi),y = find(v.se);
if( x == y )
{
tmp.push_back(mp(-1,-1)); //已经在同一集合中,不用再连了
continue;
}
if( siz[x] > siz[y] ) swap(x,y); //一定是小的连向大的
tmp.push_back(mp(x,y)); //把信息存入栈中
parent[x] = y; //合并一下
siz[y] += siz[x];
}
void undone() //反悔操作
{
pii z = tmp.back(); //取出栈顶元素
tmp.pop_back();
if( z.fi == -1 ) continue; //那时候没有操作,那么也不需要回退
parent[z.fi] = z.fi; //直接回退,做了啥就别做啥
siz[z.se] -= siz[z.fi];
}