题解:
用Splay维护欧拉序。
欧拉序有如下性质:
1.深度随着距离加减一。
2.两点间(任意出入栈序)的深度最小值为lca的深度加一。
3.出入栈之间为整个子树。
有了如上性质,直接维护欧拉序即可。注意三操作可以直接在Splay 上二分从而少一个 logn log n 。
#include <bits/stdc++.h>
using namespace std;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=4e5+50;
int n,q,ind;
vector <int> edge[N];
struct node {
node *fa,*lc,*rc;
int dep,tag,id,mn,mx;
node():fa(NULL),lc(NULL),rc(NULL){}
inline void upt() {
mn=(mx=dep);
if(lc) mn=min(mn,lc->mn), mx=max(mx,lc->mx);
if(rc) mn=min(mn,rc->mn), mx=max(mx,rc->mx);
}
inline void dec(int v) {tag+=v; mn-=v; mx-=v; dep-=v;}
inline void pushdown() {
if(!tag) return;
if(lc) lc->dec(tag);
if(rc) rc->dec(tag);
tag=0;
}
}Pool[N],*pool=Pool,*bg[N],*ed[N],*rt;
inline node* newnode(int dep,int id) {
++pool;
pool->dep=dep; pool->id=id;
pool->mn=(pool->mx=dep);
return pool;
}
inline void rotate(node *x) {
node *y=x->fa, *z=y->fa;
if(z) (z->lc==y?z->lc:z->rc)=x;
y->fa=x; x->fa=z;
if(y->lc==x) {
node *b=x->rc;
x->rc=y; y->lc=b;
if(b) b->fa=y;
} else {
node *b=x->lc;
x->lc=y; y->rc=b;
if(b) b->fa=y;
} y->upt(); x->upt();
}
inline bool which(node *x) {return x->fa->lc==x;}
inline void splay(node *x,node *tar=NULL) {
static node* sta[N]; static int tl;
sta[tl=1]=x;
for(node *y=x; y->fa; y=y->fa) sta[++tl]=y->fa;
for(int i=tl;i;i--) sta[i]->pushdown();
while(x->fa!=tar) {
node *y=x->fa;
if(y->fa!=tar) {
if(which(x)==which(y)) rotate(y);
else rotate(x);
} rotate(x);
}
if(!tar) rt=x;
}
inline node* inc(node *&now,node *v) {
if(!now) {now=v; return v;}
inc(now->rc,v); now->rc->fa=now; now->upt();
return v;
}
inline void dfs(int x,int f) {
bg[x]=newnode((!f)?1:bg[f]->dep+1,x);
splay(inc(rt,bg[x]));
for(int e=0;e<edge[x].size();e++) {
int v=edge[x][e]; if(v==f) continue;
dfs(v,x);
}
ed[x]=newnode((!f)?1:bg[f]->dep+1,x);
splay(inc(rt,ed[x]));
}
inline node* find(node *k,int v) {
k->pushdown();
if(k->lc && k->lc->mn<=v) return find(k->lc,v);
if(k->dep==v) return k;
return find(k->rc,v);
}
inline node* find_pre(node *k) {
splay(k); k=k->lc;
while(k->rc) k=k->rc;
return splay(k),k;
}
inline node* find_suf(node *k) {
splay(k); k=k->rc;
while(k->lc) k=k->lc;
return splay(k),k;
}
int main() {
n=rd(), q=rd();
for(int i=1;i<=n;i++) {
int x=rd();
while(x--) edge[i].push_back(rd());
}
dfs(1,0);
while(q--) {
int op=rd(),x=rd();
if(op==1) {
int y=rd(),v=0;
splay(bg[x]); splay(ed[y],rt); v=bg[x]->dep+ed[y]->dep;
if(bg[x]->lc && bg[x]->lc==ed[y]) {swap(x,y); splay(bg[x]); splay(ed[y],rt);}
printf("%d\n",v-2*(ed[y]->lc->mn-1));
} else if(op==2) {
int y=rd(), tar;
splay(ed[x]); tar=ed[x]->dep-y;
node *now=ed[x]->rc;
now=find(now,tar); splay(now);
node *t1=find_pre(bg[x]), *t2=find_suf(ed[x]);
splay(t1); splay(t2,rt);
node *t3=t2->lc; t2->lc=NULL; t3->fa=NULL;
t2->upt(); t1->upt(); t3->dec(y-1);
node *t4=find_pre(ed[now->id]);
splay(t4); splay(ed[now->id],rt);
rt->rc->lc=t3; t3->fa=rt->rc;
rt->rc->upt(); rt->upt();
} else {
node *now=rt; int bz=1; ++x;
do {
now->pushdown();
if(now->rc && now->rc->mx>=x) now=now->rc;
else if(now->dep==x) bz=0;
else now=now->lc;
} while(bz);
printf("%d\n",now->id);
splay(now);
}
}
}