习题:Almost Union-Find(带权冰茶姬)
题目
思路
如果只有操作1和操作3,
那么这道题就是道板的冰茶姬
只需要将元素和与大小记录在根上就行了
主要的难点在于操作2
为什么操作2很难呢?
因为如果我们将一个冰茶姬的一个元素提出来的时间复杂度不可接受
但是我们反向思考
如果删除一个元素
我们只需要\(O(1)\)的时间复杂度维护这一个冰茶姬
对于另一个冰茶姬也只需要\(O(1)\)的时间复杂度合并
同时我们考虑因为元素在原来的冰茶姬里面已经删除了
所以这个元素不会再移动
但是在另一个冰茶姬里面会有影响
我们建一个hash表就行了
也就是我们对于原来的冰茶姬我们不删除元素
只加入一个新的元素到另一个冰茶姬里面就行了
对于之后涉及到元素的操作
我们只需要用hash表里面的编号进行操作就行了
最多加\(m\)个虚点
因为\(m和n\)同阶
实际的复杂度还是\(O(nlog_n)\)
代码
#include<iostream>
using namespace std;
void read(int &x)
{
x=0;
int f=1;
char c=getchar();
while('0'>c||c>'9')
{
if(c=='-')
f=-1;
c=getchar();
}
while('0'<=c&&c<='9')
{
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
x*=f;
}
void write(long long x)
{
if(x>9)
write(x/10);
putchar(x%10+'0');
}
int n,m;
int cnt;
int h[100005];
int fa[200005];
int siz[200005];
long long s[200005];
void makeset(int n)
{
for(int i=1;i<=n;i++)
{
fa[i]=i;
h[i]=i;
s[i]=i;
siz[i]=1;
}
}
void add(int u,int w,int add)
{
siz[u]+=w;
s[u]+=add;
}
int findset(int u)
{
if(fa[u]==u)
return u;
fa[u]=findset(fa[u]);
return fa[u];
}
void unionset(int a,int b)
{
int u=findset(a);
int v=findset(b);
if(u==v)
return;
fa[u]=v;
siz[v]+=siz[u];
s[v]+=s[u];
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
cnt=n;
makeset(n+m);
for(int i=1,opt,u,v;i<=m;i++)
{
read(opt);
if(opt==1)
{
read(u);
read(v);
unionset(h[u],h[v]);
}
if(opt==2)
{
read(u);
read(v);
int fau=findset(h[u]);
int fav=findset(h[v]);
if(fau==fav)
continue;
add(fau,-1,-u);
add(fav,1,u);
h[u]=++cnt;
s[cnt]=u;
fa[h[u]]=h[v];
}
if(opt==3)
{
read(u);
int fau=findset(h[u]);
write(siz[fau]);
putchar(' ');
write(s[fau]);
putchar('\n');
}
}
}
return 0;
}