题解 【HEOI2016】tree树
题面
解析
其实这题可以考虑离线做法,用并查集解决。
因为仔细想,添加标记并不方便,
但如果用并查集记录下祖先,
再一一删除,就会方便很多。
先把每次操作记录下来,
同时记录下每个点被标记的次数(因为有多次标记,所以不能只用bool)。
然后dfs遍历,记录祖先。
再倒序处理,
当一个点的标记被删完时,就把它的并查集指向它的父亲,
并统计答案。
最后输出就行了!
具体看代码:
#include<bits/stdc++.h> using namespace std; inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return f*sum; } struct node{ int to,next; }e[1000001]; struct hh{ int fa/*爸爸*/,tag/*标记*/; int col/*并查集*/; }a[100001]; struct que{ int id/*节点*/,opt/*操作*/; int ans; }qe[100001]; int head[100001],cnt=0; int n,q; inline void add(int x,int y){ a[y].fa=x; e[++cnt].to=head[x]; e[cnt].next=y; head[x]=cnt; } int find(int x){ return a[x].col==x? x:find(a[x].col); } void dfs(int x,int fa){ a[x].fa=fa; if(a[x].tag) a[x].col=x; else a[x].col=a[fa].col; for(int i=head[x];i;i=e[i].to){ int k=e[i].next; if(k==fa) continue; dfs(k,x); } } int main(){ // freopen("tree.in","r",stdin); // freopen("tree.out","w",stdout); n=read();q=read(); a[1].tag=1; for(int i=1;i<n;i++){ int x=read(),y=read(); add(x,y); } for(int i=1;i<=q;i++){ char opt; cin>>opt; qe[i].id=read(); if(opt=='C'){ qe[i].opt=0; a[qe[i].id].tag++; } else if(opt=='Q'){ qe[i].opt=1; } } dfs(1,1); for(int i=q;i>=1;i--){ if(!qe[i].opt){ a[qe[i].id].tag--; if(!a[qe[i].id].tag) a[qe[i].id].col=a[a[qe[i].id].fa].col; } else{ qe[i].ans=find(a[qe[i].id].col); } } for(int i=1;i<=q;i++){ if(qe[i].opt){ printf("%d\n",qe[i].ans); } } return 0; }