(n,Q1e5)
线段树轻松A
并查集离线做法
先统计所有的点标记次数
先一遍dfs,若标记,父亲为自己,否则为树上的父亲
询问离线,倒序询问
若查询,直接查并查集父亲
否则减标记,若标记为0,则将并查集父亲设为树上父亲
时间复杂度(O(nlogn))
并查集直接路径压缩才是正确复杂度,因为并查集父亲方向均向上,且代表意思为祖先最近标记点,所以修改时不会错
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=1e5+4;
int n,Q,c[N],fa[N],uf[N],op[N],uu[N],ans[N];
vector<int>e[N];
void dfs(int x){
if(c[x])uf[x]=x;
else uf[x]=fa[x];
for(auto v:e[x]){
if(v==fa[x])continue;
fa[v]=x;
dfs(v);
}
}
int find(int x){return uf[x]==x?x:uf[x]=find(uf[x]);}
char ch[4];
int main(){
n=read();Q=read();
for(int i=1,u,v;i<n;i++){
u=read();v=read();
e[u].push_back(v);e[v].push_back(u);
}
c[1]=1;
for(int i=1;i<=Q;i++){
scanf("%s",ch);
op[i]=(ch[0]=='C');
uu[i]=read();
if(op[i])c[uu[i]]++;
}
dfs(1);
for(int i=Q;i;i--){
if(op[i]&&!(--c[uu[i]]))uf[uu[i]]=fa[uu[i]];
else ans[i]=find(uu[i]);
}
for(int i=1;i<=Q;i++)
if(!op[i])cout<<ans[i]<<"
";
return (0-0);
}