第五题:
题目描述
Mirko在玩堆栈游戏。开始他有一个空的堆栈,编号为0.在第i步(1<=i<=300000),他会选择一个编号为v的堆栈,复制一份并做如下操作:
1.a v 表示将v号堆栈复制一份,新栈的编号为i,并将元素i压入新栈的栈顶。
2. b v 表示将v号堆栈复制一份,新栈的编号为i,将新栈的栈顶元素弹出。
3.c v w 将v号堆栈复制一份,编号为i,并比较第v号和第w号堆栈中有多少相同的数。
输入
输入格式:第一行一个整数n,表示有n步。
接下来n步,每步表示一个操作,如上所述。
输出
输出格式:
对所有的b操作和c操作,输出结果,每行一个。b操作需要输出该栈移除的元素,c操作表示两个堆栈的相同的数的个数。
其实就是一个lca
a x就是在x后面增加一个儿子,节点编号是i
b x就是输出find(x),然后unite(i,f[x])->并查集
c x y就是输出lca(find(x),find(y)),然后unite(i,x)
注意lca是求的祖先在树中的深度,深度的维护在a x那儿,增加儿子,儿子的深度是爸爸的深度+1
A了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int fa[300010][20],f[300010],dep[300010],cnt,n;
int find(int x)
{
return (x==f[x])?x:f[x]=find(f[x]);
}
inline void unite(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)f[fx]=fy;
}
inline void adjust(int &u,int val)
{
for(int j=19;j>=0;--j)
if(dep[fa[u][j]]>=val)
u=fa[u][j];
}
inline int lca(int u,int v)
{
if(dep[u]>dep[v])adjust(u,dep[v]);
else if(dep[u]<dep[v])adjust(v,dep[u]);
if(u==v)return dep[u];
for(int j=19;j>=0;--j)
if(fa[u][j]!=fa[v][j])
u=fa[u][j],v=fa[v][j];
return dep[fa[u][0]];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
f[i]=i;
for(int i=1;i<=n;++i)
{
char op[3];
int u,v;
scanf("%s",op);
if(op[0]=='a')
{
scanf("%d",&u);
u=find(u);
fa[i][0]=u;
dep[i]=dep[u]+1;
for(int j=1;j<20&&fa[i][j-1];j++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
else if(op[0]=='b')
{
scanf("%d",&u);
u=find(u);
printf("%d\n",u);
unite(i,fa[u][0]);
}
else if(op[0]=='c')
{
scanf("%d%d",&u,&v);
u=find(u);
v=find(v);
printf("%d\n",lca(u,v));
unite(i,u);
}
}
}