题目
n(n<=1e6)个家庭,编号分别为1到n,初始下第i个部落只有第i个家庭,代表第0次操作后的版本,
以下m(m<=1e6)次操作,操作分5种,
1 k a b,把第k次操作后的a所在的部落和b所在的部落合并
2 k a,把第k次操作后的a所在的家庭删除
3 k a b,把第k次操作后a家庭移动到b家庭所在的部落
4 k a b,判断第k次操作的a家庭和b家庭是否在同一部落
5 k a,询问第k次操作的a家庭所在的部落的家庭数
特别地,如果a家庭和b家庭已经在同一部落里,忽略操作1-3,
如果a家庭已经被删除或b家庭已经被删除,忽略操作1-3,操作4的答案是No,操作5的答案是0
思路来源
https://blog.youkuaiyun.com/qq_43202683/article/details/104101765
https://blog.youkuaiyun.com/Code92007/article/details/106894293
题解
要支持一个第k版本后的可持久化并查集,但可持久化并查集复杂度是n(logn)^2
于是,考虑本题无修改,用离线做,可撤销并查集,
带删并查集是新开一个点挪出来,并把原来那棵树的root的sz减1即可,uva11987原题既视感
如果i是由第j个操作后衍生出来的,令j->i连一条边,代表树上j是i的父亲,
0显然是根节点,进入时修改,离开时撤销即可
这里把修改操作写在子树v里,因为根节点0无修改和撤销,且讨论更加方便
这样可以写成①修改v②dfs(v)③撤销v
由于涉及撤销,故不能用路径压缩,需用按秩合并,可参考思路来源的代码,
自己写了一个对u修改的,于是需要多维护若干变量讨论,
WA了好几发,终于混过去了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N=2e6+10;
vector<int>E[N];
struct node{
int op,a,b,rt1,rt2,add,fa,pre,del;
//rt1和rt2(unite之前的两个根x和y) add(unite是否令rank+1) fa(在删之前的id[a]的父亲) pre(在删之前的id[a]) del(本次是否删除了a)
node(){
a=b=rt1=rt2=fa=pre=-1;//不存在
add=del=0;
}
}e[N];
int n,m,fa;
int par[N],sz[N],id[N],rk[N],c;
int ans[N];
bool non[N];//non[i]=1表示i已经被删了
void init(int n){
for(int i=1;i<=n;++i){
par[i]=id[i]=i;
sz[i]=rk[i]=1;
}
c=n;
}
int find(int x){
return par[x]==x?x:find(par[x]);
}
void chg(int i,int x){
int fa=find(id[x]);
sz[fa]--;
e[i].fa=fa;
e[i].pre=id[x];
id[x]=++c;
par[c]=c;
if(e[i].op==2 && non[x]==0)non[x]=1,e[i].del=1;
sz[c]=rk[c]=1;
}
void unite(int i,int x,int y){
x=find(x),y=find(y);
if(x==y)return;
if(rk[x]<rk[y])swap(x,y);
par[y]=x;
sz[x]+=sz[y];
if(rk[x]==rk[y])rk[x]++,e[i].add=1;
e[i].rt1=x;e[i].rt2=y;
}
bool same(int x,int y){
x=find(x),y=find(y);
return x==y;
}
void apply(int i){
int &op=e[i].op,&a=e[i].a,&b=e[i].b;
if(op==1){
if(non[a]||non[b])return;
if(same(id[a],id[b]))return;
unite(i,id[a],id[b]);
}
else if(op==2){
if(non[a])return;
chg(i,a);
}
else if(op==3){
if(non[a]||non[b])return;
if(same(id[a],id[b]))return;
chg(i,a);
unite(i,id[a],id[b]);
}
else if(op==4){
if(non[a]||non[b])return;
ans[i]=same(id[a],id[b]);
}
else if(op==5){
if(non[a])return;
ans[i]=sz[find(id[a])];
}
}
void cancel(int i){
int &op=e[i].op,&a=e[i].a,&b=e[i].b;
int &rt1=e[i].rt1,&rt2=e[i].rt2,&fa=e[i].fa,&pre=e[i].pre,&add=e[i].add;
if(op==1){
if(non[a]||non[b])return;
if(rt1==-1)return;
if(add)rk[rt1]--;
par[rt2]=rt2;
sz[rt1]-=sz[rt2];
}
else if(op==2){
if(non[a] && e[i].del==0)return;
if(fa==-1)return;
id[a]=pre;
sz[fa]++;
if(non[a] && e[i].del==1)non[a]=0;
}
else if(op==3){
if(non[a]||non[b])return;
if(rt1==-1||fa==-1)return;
if(add)rk[rt1]--;
par[rt2]=rt2;
sz[rt1]-=sz[rt2];
id[a]=pre;
sz[fa]++;
}
}
void dfs(int u){
apply(u);
for(int v:E[u]){
dfs(v);
}
cancel(u);
}
int main(){
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&e[i].op,&fa,&e[i].a);
if(e[i].op!=2 && e[i].op!=5){
scanf("%d",&e[i].b);
}
E[fa].pb(i);
}
dfs(0);
for(int i=1;i<=m;++i){
if(e[i].op==4){
puts(ans[i]?"Yes":"No");
}
else if(e[i].op==5){
printf("%d\n",ans[i]);
}
}
return 0;
}