Codeforces Round#520 div2 E. Company [LCA] [线段树] [dfs序]

【题目】:给你一颗由n个点组成的树,编号为1-n中的一个数字,并且各不相同。一共有q个询问,每次询问给你一个区间[L,R],让你求出在可以忽略区间中一个点的情况下,他们深度最大的lca是谁,并且输出忽略的是谁,lca是谁。
链接:E. Company
【题解】:结论:对这棵树进行dfs序,然后一个区间的LCA一定是dfs序最小的点和dfs序最大的点的LCA。
证明挺简单的 这里忽略。
那么知道了这个结论之后我们只要先用线段树维护好区间最小的和最大的dfs序,每次询问到一个区间时,把dfs序最大的点拿掉,然后找第二大的点,求它和dfs序最小的点的lca,然后再把第一大的点放回来,再把第一小的点拿掉,再找到第二小的点和第一大的点求LCA,把两个答案进行比较后输出答案即可。

【代码】:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 100000+30;
int cnt=1,head[maxn],E[maxn*2];
struct Node
{
    int u,v,w,nxt;
}node[maxn*2];

void add(int u,int v,int w)
{
    node[cnt].u=u;
    node[cnt].v=v;
    node[cnt].w=w;
    node[cnt].nxt=head[u];
    head[u]=cnt++;
}
int dfn[maxn],dep[maxn],st[maxn*2][25];
void dfs(int rt,int fa,int d)
{
    dfn[rt]=++cnt;
    E[cnt]=rt;
    dep[rt]=d;
    for(int i=head[rt];i;i=node[i].nxt){
        dfs(node[i].v,rt,d+1);
        E[++cnt]=rt;
    }
}
void buildST()
{
    for(int i=1;i<=cnt;i++){
        st[i][0]=E[i];
    }
    for(int i=1;i<=24;i++){
        for(int j=1;j<=cnt;j++){
            if(j+(1<<i)-1>cnt) break;
            if(dep[st[j][i-1]]<dep[st[j+(1<<(i-1))][i-1]]) st[j][i]=st[j][i-1];
            else st[j][i]=st[j+(1<<(i-1))][i-1];
        }
    }
}
int lca(int a,int b)
{
    int x=a,y=b;
    if(x>y) swap(x,y);
    int j=1;
    int cnt=0;
    while(j*2<=y-x+1){
        j<<=1;
        cnt++;
    }
    if(dep[st[x][cnt]]<dep[st[y-j+1][cnt]]) return st[x][cnt];
    else return st[y-j+1][cnt];
}
int maxP[maxn*8],minP[maxn*8];
void build(int rt,int L,int R)
{
    if(L==R){
        maxP[rt]=dfn[L];
        minP[rt]=dfn[L];
    }
    else{
        int mid=L+R>>1;
        build(rt<<1,L,mid);
        build(rt<<1|1,mid+1,R);
        maxP[rt]=max(maxP[rt<<1],maxP[rt<<1|1]);
        minP[rt]=min(minP[rt<<1],minP[rt<<1|1]);
    }
}
int queryMax(int rt,int L,int R,int l,int r)
{
    if(L>=l&&R<=r) return maxP[rt];
    int mid=L+R>>1;
    if(mid>=r){
        return queryMax(rt<<1,L,mid,l,r);
    }
    else if(mid<l){
        return queryMax(rt<<1|1,mid+1,R,l,r);
    }
    else{
        return max(queryMax(rt<<1,L,mid,l,mid),queryMax(rt<<1|1,mid+1,R,mid+1,r));
    }
}
int queryMin(int rt,int L,int R,int l,int r)
{
    if(L>=l&&R<=r) return minP[rt];
    int mid=L+R>>1;
    if(mid>=r){
        return queryMin(rt<<1,L,mid,l,r);
    }
    else if(mid<l){
        return queryMin(rt<<1|1,mid+1,R,l,r);
    }
    else{
        return min(queryMin(rt<<1,L,mid,l,mid),queryMin(rt<<1|1,mid+1,R,mid+1,r));
    }
}
void update(int rt,int L,int R,int v,int x)
{
    if(L==R){
        maxP[rt]=minP[rt]=v;
    }
    else{
        int mid=L+R>>1;
        if(x<=mid){
            update(rt<<1,L,mid,v,x);
        }
        else{
            update(rt<<1|1,mid+1,R,v,x);
        }
        maxP[rt]=max(maxP[rt<<1],maxP[rt<<1|1]);
        minP[rt]=min(minP[rt<<1],minP[rt<<1|1]);
    }
}
int main()
{
    //printf("%d\n",1<<25);
    int n,q,x;
    scanf("%d%d",&n,&q);
    for(int i=2;i<=n;i++){
        scanf("%d",&x);
        add(x,i,1);
    }
    cnt=0;
    dfs(1,0,0);
    buildST();
    build(1,1,cnt);
    int l,r;
    for(int i=0;i<q;i++){
        scanf("%d%d",&l,&r);
        int a=queryMax(1,1,cnt,l,r);
        int b=queryMin(1,1,cnt,l,r);
        int p1=E[a],p2=E[b];

        update(1,1,cnt,0,p1);
        int t=queryMax(1,1,cnt,l,r);
        int ans1=lca(t,b);
        update(1,1,cnt,a,p1);

        update(1,1,cnt,maxn*100,p2);
        t=queryMin(1,1,cnt,l,r);
        int ans2=lca(t,a);
        update(1,1,cnt,b,p2);
        
        if(dep[ans1]>dep[ans2]){
            printf("%d %d\n",p1,dep[ans1]);
        }
        else{
            printf("%d %d\n",p2,dep[ans2]);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值