题意:
给出一棵树,有两种操作:
C x:标记点x;
Q x:查询某个点的最近被标记祖先;
n,m<=100000;
题解:
首先我们发现如果标记了一个点,其影响是对于个子树,也就是一段DFS区间的;
那么我们可以转化成一个序列上的问题:区间加入一个值,单点查询最大值;
然后直接标记永久化搞个线段树套set就可以了,时间复杂度O(nlog^2n);
【我怎么突然感觉不用套set直接维护最小值就行呢。。。
代码:
#include<set> #include<stdio.h> #include<string.h> #include<algorithm> #define N 110000 #define pr pair<int,int> #define lson l,mid,no<<1 #define rson mid+1,r,no<<1|1 using namespace std; typedef long long ll; char op[N]; int next[N<<1],to[N<<1],head[N],ce; int L[N],R[N],deep[N],tim; bool cov[N]; set<pr>tr[N<<2]; void add(int x,int y) { to[++ce]=y; next[ce]=head[x]; head[x]=ce; } void dfs(int x,int pre) { L[x]=++tim; deep[x]=deep[pre]+1; for(int i=head[x];i;i=next[i]) { if(to[i]!=pre) { dfs(to[i],x); } } R[x]=tim; } pr getmax(const set<pr> &s) { if(!s.size()) return pr(0,0); return *(--s.end()); } void update(int l,int r,int no,int st,int en,pr val) { if(st<=l&&r<=en) { tr[no].insert(val); } else { int mid=l+r>>1; if(en<=mid) update(lson,st,en,val); else if(st>mid) update(rson,st,en,val); else update(lson,st,en,val),update(rson,st,en,val); } } pr query(int l,int r,int no,int k) { if(l==r) return getmax(tr[no]); else { int mid=l+r>>1; if(k<=mid) return max(query(lson,k),getmax(tr[no])); else return max(query(rson,k),getmax(tr[no])); } } int main() { int n,m,i,j,k,x,y; scanf("%d%d",&n,&m); for(i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y),add(y,x); } dfs(1,0); cov[1]=1; tr[1].insert(pr(1,1)); for(i=1;i<=m;i++) { scanf("%s%d",op,&x); if(op[0]=='C') { if(!cov[x]) { update(1,n,1,L[x],R[x],pr(deep[x],x)); cov[x]=1; } } else { printf("%d\n",query(1,n,1,L[x]).second); } } return 0; }