题目描述:
给出一棵树
有M个操作
C U 表示给 U节点打标记
Q U 表示询问 距离 U节点最近的标记节点(包括本身
初始时只有1节点有标记 一个点可以重复多次打标记
题目分析:
一开始傻敷敷的打了个树剖+线段树
然而题目并没有强制要求在线搞…
我们把所有操作读进来
cnt[i]表示i节点目前标记数目
我们先遍历树,搞出父子关系
然后用并查集
如果一个点 cnt[i]!=0
fa[i]=i
反之 fa[i]=f[i]//f[i]表示i节点的树上父亲
这样我们倒着处理,把加标记变成删标记
当一个点的标记删完了,那么就把他的并查集父亲改成树上父亲即可
题目链接:
Ac 代码:
#include <iostream>
#include <cstdio>
const int maxm=100005;
int head[maxm],net[maxm*2],to[maxm*2],Cnt;
int f[maxm];
void addedge(int u,int v)
{
Cnt++;
to[Cnt]=v,net[Cnt]=head[u],head[u]=Cnt;
}
int cnt[maxm],fa[maxm],ans[maxm];
struct{
int opt,id;
}ask[maxm];
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int n,q;
void dfs(int now,int fax)
{
f[now]=fax;
for(int i=head[now];i;i=net[i])
if(to[i]!=fax) dfs(to[i],now);
}
int main()
{
//freopen("ts.in","r",stdin);
scanf("%d%d",&n,&q);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
for(int i=1;i<=q;i++)
{
char s[10];
scanf("%s%d",s,&ask[i].id);
if(s[0]=='Q') ask[i].opt=1;
else ask[i].opt=0,cnt[ask[i].id]++;
}
dfs(1,1);
for(int i=1;i<=n;i++) fa[i]=cnt[i]?i:f[i];
for(int i=q;i>=1;i--)
{
//printf("%d %d\n",i,ask[i].opt);
if(ask[i].opt) ans[i]=find(ask[i].id);
else
{
cnt[ask[i].id]--;
if(!cnt[ask[i].id]) fa[ask[i].id]=find(f[ask[i].id]);
}
}
for(int i=1;i<=q;i++)
if(ask[i].opt) printf("%d\n",ans[i]);
return 0;
}