中文题意不再赘述,说一下大体思路
网上流传的做法是并查集和树链剖分,我太弱只会并查集,这道题正常思路的并查集肯定做不了,因为要维护一个动态的关系,回想一下,之前我发过的一片博文亲戚,里面用到了并查集的离线处理,就是先将所有操作读入,需要修改的操作都做掉,然后从最后一个操作开始往回做,修改也就是说删边往回做的时候相当于加边,加边总比删边简单,所以离线倒序处理并查集就好,然后呢,这道题又有和一般并查集不一样的地方,我们需要把每个元素的父节点附成离他最近的一个元素,如何处理呢?dfs,如果当前点没有被标记,那么就把它的父节点赋为他父亲的父节点,用dfs遍历图,得到子树关系,如果当前点被标记,那么就把它的父节点附成本身,然后处理标记操作时,因为是逆序处理的操作,所以标记相当于取消标记,让它的标记--,如果它没有标记了,那么它的父节点就要变成dfs时,遍历到他的上一个点,就这么多,看代码
//By AcerMo #include<map> #include<cmath> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int M=100500; int fa[M],siz[M],fatt[M]; int n,m,sign[M],ans[M]; vector<int>v[M]; struct que { char s; int a; }qlm[M]; int find(int x) { if (x!=fa[x]) return fa[x]=find(fa[x]); return x; } void unionn(int a,int b) { if (siz[a]<=siz[b]) siz[b]+=siz[a],fa[a]=b; else siz[a]+=siz[b],fa[b]=a; return ; } void built(int x) { for (int i=0;i<v[x].size();i++) { int go=v[x][i]; if (go!=fatt[x]) fatt[go]=x,built(go); } return ; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<n;i++) { int a,b;scanf("%d%d",&a,&b); v[a].push_back(b);v[b].push_back(a); } built(1);sign[1]=1; for (int i=1;i<=m;i++) { scanf("%s %d",&qlm[i].s,&qlm[i].a); if (qlm[i].s=='C') sign[qlm[i].a]++; } for (int i=1;i<=n;i++) if (sign[i]) fa[i]=i; else fa[i]=fatt[i]; for (int i=m;i>0;i--) { if (qlm[i].s=='C') { sign[qlm[i].a]--; if (!sign[qlm[i].a]) fa[qlm[i].a]=fatt[qlm[i].a]; } else ans[i]=find(qlm[i].a); } for (int i=1;i<=m;i++) if (ans[i]) cout<<ans[i]<<endl; return 0; }