Almost Union-Find
这题的重点在于一些点会被删除,但是我们知道,如果不是根节点的还好,一旦删除根节点,该并查集的结构就被严重破坏了,因此我们选择设置一个虚点,他不存在,也不贡献点的数量,也不贡献值,仅仅是存在于那里为我们来维持结构。
为了达到这个目的,我们为每个点设定一个id值每当这个点经历了一次删除操作,我们就为他更换一个新的ip。再检索时就用这个新的ID进行检索,而旧的ID就放在那里储存和维持原来的结构。特别需要注意的是,我们总是通过点来确定,我们到底是要搜索哪个id而在后续的搜索和合并都使用id值来进行合并。即信息的储存仅与id有关,而id与点有关,但点与信息无关。
具体细节见代码:
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int size=1e6+5;
int tot;
int fa[size],id[size],cnt[size],sum[size];
void init(int n)
{
tot=n+1;
for(int i=1;i<=n;i++)
{
cnt[i]=1;
sum[i]=i;
fa[i]=i;
id[i]=i;
}
}
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
int fx=find(id[x]),fy=find(id[y]);
if(fx==fy) return;
cnt[fy]+=cnt[fx];
sum[fy]+=sum[fx];
cnt[fx]=0,sum[fx]=0;
fa[fx]=fy;
}
void move(int x,int y)
{
int fx=find(id[x]),fy=find(id[y]);
id[x]=++tot;
fa[tot]=fy;
sum[fy]+=x;
cnt[fy]++;
sum[fx]-=x;
cnt[fx]--;
}
int32_t main()
{
int n,k;
while(~scanf("%lld%lld",&n,&k))
{
init(n);
while(k--)
{
int op;
scanf("%lld",&op);
if(op==1)
{
int a,b;
scanf("%lld%lld",&a,&b);
merge(a,b);
}
if(op==2)
{
int a,b;
scanf("%lld%lld",&a,&b);
move(a,b);
}
if(op==3)
{
int x;
scanf("%lld",&x);
int fx=find(id[x]);
cout<<cnt[fx]<<' '<<sum[fx]<<endl;
}
}
}
}