题解:动态树,维护Splay最深的被标记过的点
每个询问先Access(x);
当然用树链剖分也可以
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=100009;
const int oo=1000000000;
int n,T;
int fa[maxn],ch[maxn][2],dest[maxn],v[maxn];
inline int son(int x){
if(ch[fa[x]][1]==x)return 1;
else return 0;
}
inline void pushup(int x){
dest[x]=dest[ch[x][1]];
if(!dest[x]){
if(v[x])dest[x]=x;
}
if(!dest[x])dest[x]=dest[ch[x][0]];
}
inline bool isroot(int x){
return (ch[fa[x]][0]!=x)&&(ch[fa[x]][1]!=x);
}
inline void Rotate(int x){
int y=fa[x];
int z=fa[y];
int b=son(x),c=son(y);
int a=ch[x][b^1];
if(!isroot(y))ch[z][c]=x;
fa[x]=z;
if(a)fa[a]=y;
ch[y][b]=a;
fa[y]=x;ch[x][b^1]=y;
pushup(y);pushup(x);
}
void Splay(int x){
while(!isroot(x)){
int y=fa[x];
if(isroot(y)){
Rotate(x);
}else{
if(son(x)==son(y)){
Rotate(y);Rotate(x);
}else{
Rotate(x);Rotate(x);
}
}
}
}
void Access(int x){
for(int t=0;x;t=x,x=fa[x]){
Splay(x);ch[x][1]=t;pushup(x);
}
}
int cntedge;
int head[maxn];
int to[maxn<<1],nex[maxn<<1];
void Addedge(int x,int y){
nex[++cntedge]=head[x];
to[cntedge]=y;
head[x]=cntedge;
}
queue<int>q;
int father[maxn];
void Bfs(){
q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nex[i]){
if(to[i]==father[x])continue;
father[to[i]]=x;
fa[to[i]]=x;
q.push(to[i]);
}
}
}
int main(){
scanf("%d%d",&n,&T);
for(int i=1;i<=n-1;++i){
int x,y;
scanf("%d%d",&x,&y);
Addedge(x,y);
Addedge(y,x);
}
v[1]=1;dest[1]=1;
Bfs();
while(T--){
char opty[10];
scanf("%s",opty);
int x;
scanf("%d",&x);
if(opty[0]==‘C‘){
Splay(x);v[x]=1;pushup(x);
}else{
Access(x);Splay(x);printf("%d\n",dest[x]);
}
}
return 0;
}