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;
}

 

posted on 2018-11-19 22:01  Helpp  阅读(295)  评论(0编辑  收藏  举报

导航