cf 1062e 区间lca+线段树+dfs序
http://codeforces.com/contest/1062/problem/E
题意:给出一棵有根树,1为根结点,接下来q次询问,每次给出一个[l,r]区间,
现在允许删掉[l,r]区间内任何一个点,使得所有点的最近公共祖先的深度尽可能大,
问删掉的点是哪个点,深度最大是多
思路:
区间lca的求法:
区间的LCA等价于区间dfs序的最小值和最大值对应的两个点的LCA。
(给定的一段点都可以..)
证明:
假设 r 是 这些点的lca , in[r]<=in[u]<=in[v]<=out[r] (=是可能是一个点..)
那么假设有w in[u]<=in[w]<=in[v]
很明显 in[r]<=in[w]<=out[r] 所以w是r的子树上的点
这就证明了
所以用线段树 保存in[u]的最大值,最小值得到点
在删除点的时候,假设u,v是最大和最小,那么在[l,r]删除除了u,v的其他点是无意义的
所以也就是算 [l,u-1]U[u+1,r] 和 [l,v-1,v+1,r] 那个更符合条件了
这样就是 线段树+区间Lca了
#include<bits/stdc++.h> using namespace std; #define mem(a) memset(a,0,sizeof(a)) #define time __tiem #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 const int N= 1e5+4; struct edge{ int x[N],y[N],z[N],cnt,nxt[N],fst[N]; void clc(){ cnt =0 ; mem(fst); } void add(int a,int b){ x[++cnt] = a; y[cnt] = b; nxt[cnt] =fst[a]; fst[a]= cnt; } }e; int n,m,depth[N],in[N],out[N],time; int ST[N*2][20]; int tmax[N<<2],tmin[N<<2]; int Hash[N<<2]; //先保存最值 之后再Hash一下 得到点 void pushup(int rt){ tmin[rt] = min(tmin[rt<<1],tmin[rt<<1|1]); tmax[rt] = max(tmax[rt<<1],tmax[rt<<1|1]); } void build(int l,int r,int rt){ if(l==r){ tmin[rt] =tmax[rt]= in[l]; return ; } int mid = (l+r)>>1; build(lson); build(rson); pushup(rt); } int quemin(int l,int r,int rt,int a,int b){ int mid =(l+r)>>1; int ans= 1e8+4; if(a>b)return ans; if(a<=l && b>=r)return min(ans,tmin[rt]); if(a<=mid) ans= min(ans, quemin(lson,a,b)); if(b>mid) ans= min(ans, quemin(rson,a,b )); return ans ; } int quemax(int l,int r,int rt,int a,int b){ int ans= -1; if(a>b)return -1; int mid = (l+r)>>1; if(a<=l && b>=r)return max(ans,tmax[rt]); if(a<=mid) ans= max(ans, quemax(lson,a,b)); if(b>mid) ans = max(ans, quemax(rson,a,b ) ); return ans ; } void dfs(int t){ in[t] = ++time; ST[time][0] =t; for(int i=e.fst[t];i;i=e.nxt[i]){ depth[e.y[i]] = depth[t]+1; dfs(e.y[i]); ST[++time][0]=t; } out[t] =time; } void Get_ST(int n){ for (int i=1;i<=n;i++) for (int j=1;j<20;j++){ ST[i][j]=ST[i][j-1]; int v=i-(1<<(j-1)); if (v>0&&depth[ST[v][j-1]]<depth[ST[i][j]]) ST[i][j]=ST[v][j-1]; } } int RMQ(int L,int R){ int val=floor(log(R-L+1)/log(2)); int x=ST[L+(1<<val)-1][val],y=ST[R][val]; if (depth[x]<depth[y]) return x; else return y; } int getmin(int a,int b,int c,int d){ int res=quemin(1,n,1,a,b); return Hash[ min(res, quemin(1,n,1,c,d)) ]; } int getmax(int a,int b,int c,int d){ int res = quemax(1,n,1,a,b); return Hash[max(res,quemax(1,n,1,c,d))]; } int main(){ int q; scanf("%d %d",&n,&q); int x; for(int i=2;i<=n;++i){ scanf("%d",&x); e.add(x,i); } dfs(1); Get_ST(time); build(1,n,1); for(int i=1;i<=n;++i){ Hash[in[i]]=i; } while(q--){ int l,r; scanf("%d %d",&l,&r); //区间Lca int u = quemin(1,n,1,l,r); int v = quemax(1,n,1,l,r); u = Hash[u]; v = Hash[v]; int Min = getmin(l,u-1,u+1,r);//(4,5) (7,6) --5 int Max = getmax(l,v-1,v+1,r);//(4,3) (5,6) --** 5 if(Min==-1 || Min>1e6)Min =v,cerr<<"??"<<endl; if(Max==-1 || Max>1e6)Max =u,cerr<<"??"<<endl; int lca1 = RMQ(in[Min],in[v]); int lca2= RMQ(in[u],in[Max]); if(depth[lca1]>depth[lca2])printf("%d %d\n",u,depth[lca1]); else printf("%d %d\n",v,depth[lca2]); } return 0; }