Description
在2016年,佳媛姐姐刚刚学习了树,非常开心。
现在她想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:
1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记);
2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
先).
你能帮帮她吗?
Input
输入第一行两个正整数和,分别表示节点个数和操作次数.
接下来行,每行两个正整数,表示到有一条有向边.
接下来行,形如“”.为“”时,表示这是一个标记操作;
为“”时,表示这是一个询问操作.
Output
输出一个正整数,表示结果.
Sample Input
5 5
1 2
1 3
2 4
2 5
Q 2
C 2
Q 2
Q 5
Q 3
Sample Output
1
2
2
1
HINT
Solution
正难则反,对于所有操作逆序离线处理.
先记录每个节点被打过几次标记,预处理出每个节点最后时刻最近的打了标记的祖先.
从后往前消除标记,如果当前点的标记被消除完,则这个点最近的打了标记的祖先为它父亲最近的打了标记的祖先,并查集可实现.
#include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> #define N 100005 using namespace std; struct graph{ int nxt,to; }e[N<<1]; struct quest{ int t,x; }b[N]; int a[N],f[N],g[N],fa[N],ans[N],n,m,q,t,x,cnt; stack<int> s; inline int read(){ int ret=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)){ ret=(ret<<1)+(ret<<3)+c-'0'; c=getchar(); } return ret; } inline int rc(){ char c=getchar(); while(c!='Q'&&c!='C') c=getchar(); if(c=='C') return 1; return 2; } inline void addedge(int x,int y){ e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y; } inline void dfs(int u){ fa[u]=f[u]=1;s.push(u); while(!s.empty()){ u=s.top();s.pop(); for(int i=g[u];i;i=e[i].nxt) if(fa[u]!=e[i].to){ fa[e[i].to]=u;s.push(e[i].to); if(!a[e[i].to]) f[e[i].to]=f[u]; else f[e[i].to]=e[i].to; } } } inline int gf(int k){ if(f[k]==k) return k; return f[k]=gf(f[k]); } inline void init(){ n=read();q=read(); for(int i=1,j,k;i<n;++i){ j=read();k=read(); addedge(j,k);addedge(k,j); } for(int i=1;i<=q;++i){ b[i].t=rc();b[i].x=read(); if(b[i].t&1) ++a[b[i].x]; } ++a[1];dfs(1);cnt=0; for(int i=q,j,k;i;--i){ if(b[i].t&1){ if(!(--a[b[i].x])){ f[b[i].x]=gf(f[fa[b[i].x]]); } } else ans[++cnt]=gf(f[b[i].x]); } for(int i=cnt;i;--i) printf("%d ",ans[i]); } int main(){ freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); init(); fclose(stdin); fclose(stdout); return 0; }