bzoj 4551 树 Posted on 2019-07-06 | Views: 并查集. 大力用树剖 + 树状数组维护是 $O(n\cdot log^3n)$ 的. 考虑先将操作先全部离线下来,每个标记都打上,对于没有被打标记的点,用并查集将它与它的父亲合并. 然后从后往前处理操作,遇到询问就直接回答,遇到修改,其实是给某个点的标记次数 $-1$ ,删除后用并查集合并. 时间复杂度 $O(n)$ . 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105#include<bits/stdc++.h>using namespace std;typedef long long ll;inline int read(){ int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int MAXN=1e5+10;int n,Q;char buf[2];int head[MAXN],to[MAXN],nx[MAXN],ecnt=0;void addedge(int u,int v){ ++ecnt; to[ecnt]=v; nx[ecnt]=head[u]; head[u]=ecnt;}struct opt{ int tp,x;}q[MAXN];struct dsu{ int fa[MAXN]; void init() { for(int i=1;i<=n;++i) fa[i]=i; } int Find(int x) { if(x==fa[x]) return x; return (fa[x]=Find(fa[x])); } void Merge(int u,int v) { u=Find(u),v=Find(v); if(u!=v) fa[u]=v; }}DSU;int flag[MAXN],Fa[MAXN];void dfs(int u,int f){ Fa[u]=f; if(!flag[u]) DSU.Merge(u,f); for(int i=head[u];i;i=nx[i]) { int v=to[i]; dfs(v,u); }}int ans[MAXN],tot=0;int main(){ n=read(),Q=read(); for(int i=1;i<n;++i) { int u=read(),v=read(); addedge(u,v); } flag[1]=1; for(int i=1;i<=Q;++i) { scanf("%s",buf); q[i].tp=(buf[0]=='C'); q[i].x=read(); if(q[i].tp) ++flag[q[i].x]; else ++tot; } int pt=tot; DSU.init(); dfs(1,0); for(int i=Q;i>=1;--i) { int tp=q[i].tp,x=q[i].x; if(!tp) { x=DSU.Find(x); ans[pt--]=x; } else { --flag[x]; if(!flag[x]) DSU.Merge(x,Fa[x]); } } for(int i=1;i<=tot;++i) printf("%d\n",ans[i]); return 0;}