题目大意
给出一棵有根树,有很多操作,给一个点打标记,查询到跟路上最近那个点打了标记。
题解
我的做法是先搞出dfs序,打标记只会影响子树,线段树区间修改就行了,查询就是单点查询。注意空点不下传标记,不然空间不够。
更好的做法是离线用并查集维护。打标记相当于把一棵子树从整棵树中断开,倒着做,先断开子树。查询就是getfather,答案就是father的father。修改就是合并子树,并查集连一下,应为修改可重复,遇到正数第一次修改才合并。
code
#include<set>
#include
#include
#include
#include
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=100000;
int n,q,gra,time,to[maxn*2+10],next[maxn*2+10],begin[maxn+10],dfn[maxn+10],leave[maxn+10],pon[maxn+10],
ans[maxn*5+10],tag[maxn*5+10];
void insert(int u,int v){
to[++gra]=v;
next[gra]=begin[u];
begin[u]=gra;
}
void dfs(int now,int pre){
dfn[now]=++time;
pon[time]=now;
for(int i=begin[now];i;i=next[i])
if(to[i]!=pre)dfs(to[i],now);
leave[now]=time;
}
void pushtag(int now,int tg){
if(tag[now]){
ans[now]=max(ans[now],tag[now]);
if(tg){
tag[now*2]=max(tag[now*2],tag[now]);
tag[now*2+1]=max(tag[now*2+1],tag[now]);
}
tag[now]=0;
}
}
void change(int now,int l,int r,int l1,int r1,int val){
pushtag(now,l!=r);
int m=(l+r)/2;
if((l==l1)&&(r==r1))tag[now]=max(tag[now],val);
else if(r1<=m)change(now*2,l,m,l1,r1,val);
else if(mnow*2+1,m+1,r,l1,r1,val);
else{
change(now*2,l,m,l1,m,val);
change(now*2+1,m+1,r,m+1,r1,val);
}
}
int get(int now,int l,int r,int pos){
pushtag(now,l!=r);
int m=(l+r)/2;
if(l==r)return ans[now];
else if(pos<=m)return get(now*2,l,m,pos);
else return get(now*2+1,m+1,r,pos);
}
int main(){
scanf("%d%d",&n,&q);
fo(i,1,n-1){
int u,v;scanf("%d%d\n",&u,&v);
insert(u,v);insert(v,u);
}
dfs(1,0);
change(1,1,n,1,n,1);
fo(i,1,q){
char oper;int num;scanf("%c%d\n",&oper,&num);
if(oper==
else printf("%d\n",pon[get(1,1,n,dfn[num])]);
}
return 0;
}