【 Codeforces Round #520 (Div. 2) E. Company】dfs序+线段树+lca

本文探讨了在树形数据结构中进行区间查询的多种高效算法,重点介绍了利用线段树和ST表进行区间最低公共祖先(LCA)深度最大化查询的方法。通过三种不同策略——基于区间LCA和DFS序维护、直接维护区间内最大最小DFS序以及维护区间次大次小DFS序,文章详细讲解了如何优化查询过程,以达到快速准确地获取结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


E. Company

题意

给你一颗具有n个节点的树,有q次查询,每次查询给出l,r
求(l,l+1,l+2…r-1,r)这段区间不考虑哪个节点之后能让剩余节点的lca深度尽量大

做法

这道题有好多种做法,我在做的过程中用了三种做法

首先我们要知道,不考虑一个节点能让一些点的lca发生变化的话,
这个点一定是dfs序最小的点或者dfs序最大的点。
如果能想到这个结论第一种做法就很好想


第一种做法
线段树维护区间lca,st表维护区间dfs序,这样给定一段区间之后
我们就可以通过st表找到哪两个点是dfs最小和最大的点
之后线段树上求该点左侧的区间和该点右侧的区间的lca
对dfs序最小的点求一次,对dfs序最大的点求一次,再取个max就是答案


之后的做法需要知道(猜)另一个结论
一些点的lca就是dfs序最小的点和dfs序最大的点的lca


第二种做法
这样我们用线段树维护区间dfs序,
去掉dfs序最小的点之后在剩下的两个区间查找新的dfs序最小/最大的点
求他们的lca,去掉dfs序最大的点再来一遍,取个max就是答案


第三种做法
第三种做法就是基于第二种做法的基础上,我们直接维护区间最大此大最小次小,这样就可以直接查询当前l,r之内的最大最小最小次小dfs序
删除dfs序最大的点,也就是次大dfs序的点与最小dfs序的点求lca
删除dfs序最小的点,也就是次小dfs序的点与最大dfs序的点求lca
两个取个max即可


代码

第一种做法

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
const int maxn = 1e5+10;
vector<int> G[maxn];
int deep[maxn];
int f[maxn][20];
int in[maxn],mp[maxn],time;
void dfs(int u,int fa)
{
    f[u][0]=fa;
    in[u]=++time;
    mp[time]=u;
    for(int i=1; i<=19; i++)
        f[u][i]=f[f[u][i-1]][i-1];
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(v==fa)
            continue;
        deep[v]=deep[u]+1;
        dfs(v,u);
    }
}
int lca(int x,int y)
{
    if(x==-1) return y;
    if(y==-1) return x;
    if(deep[x]<deep[y]) swap(x,y);
    for (int i=19; i>=0; i--)
    {
         if(deep[x]-(1LL<<i)>=deep[y]) x=f[x][i];
    }
    if(x==y) return x;
    for(int i=19; i>=0; i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
struct T
{
    int l,r,mid;
    int LCA;
} Tree[maxn<<2];
void push_up(int rt)
{
    Tree[rt].LCA=lca(Tree[rt<<1].LCA,Tree[rt<<1|1].LCA);
}
void build(int rt,int l,int r)
{
    Tree[rt].l=l;
    Tree[rt].r=r;
    Tree[rt].LCA=-1;
    if(l==r)
    {
        Tree[rt].LCA=l;
        return ;
    }
    int mid=Tree[rt].mid=l+r>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    push_up(rt);
}
int query(int rt,int l,int r)
{
    if(l>r) return -1;
    if(Tree[rt].l>=l&&Tree[rt].r<=r) return Tree[rt].LCA;
    if(Tree[rt].l>r||Tree[rt].r<l) return -1;
    int ans=-1;
    if(l<=Tree[rt].mid) ans=query(rt<<1,l,r);
    if(r>Tree[rt].mid)   ans=lca(ans,query(rt<<1|1,l,r));

    return ans;
}
inline int min_(int x,int y)
{
    return x<y?x:y;
}
inline int max_(int x,int y)
{
    return x>y?x:y;
}
int f2[maxn][16],f3[maxn][16];
void build_st(int n)
{
    int p=0;
    for(int i=1; i<=n; i<<=1) p++;
    for(int i=1; i<=n; i++)
    {
        f2[i][0]=in[i];
        f3[i][0]=in[i];
    }
    for(int j=1; j<p; j++)
    {
        for(int i=1; i<=n; i++)
        {
            if(i+(1<<j-1)>n)
                f2[i][j]=f2[i][j-1];
            else
                f2[i][j]=max_(f2[i][j-1],f2[i+(1<<j-1)][j-1]);
        }
    }
    for(int j=1; j<p; j++)
    {
        for(int i=1; i<=n; i++)
        {
            if(i+(1<<j-1)>n) f3[i][j]=f3[i][j-1];
            else  f3[i][j]=min_(f3[i][j-1],f3[i+(1<<j-1)][j-1]);
        }
    }
}
int query_max(int l,int r)
{
    int p=log2(r-l+1);
    return max_(f2[l][p],f2[r-(1<<p)+1][p]);
}
int query_min(int l,int r)
{
    int p=log2(r-l+1);
    return min_(f3[l][p],f3[r-(1<<p)+1][p]);
}
int main()
{
    int n,q,x,y;
    scanf("%d%d",&n,&q);
    for(int i=2; i<=n; i++)
    {
        scanf("%d",&x);
        G[x].push_back(i);
        G[i].push_back(x);
    }
    dfs(1,0);// deep ok  lca ok dfsxu ok
    build_st(n);
    build(1,1,n);
    while(q--)
    {
        scanf("%d%d",&x,&y);
        int st=mp[query_min(x,y)];
        int en=mp[query_max(x,y)];
        int ans1=lca(query(1,x,st-1),query(1,st+1,y));
        int ans2=lca(query(1,x,en-1),query(1,en+1,y));
        if(deep[ans1]>deep[ans2]) printf("%d %d\n",st,max_(deep[ans1],deep[ans2]));
        else printf("%d %d\n",en,max_(deep[ans1],deep[ans2]));
    }
    return 0;
}

第二种做法

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
const int maxn = 1e5+10;
vector<int> G[maxn];
int deep[maxn];
int f[maxn][20];
int in[maxn],mp[maxn],time;
void dfs(int u,int fa)
{
    f[u][0]=fa;
    in[u]=++time;
    mp[time]=u;
    for(int i=1; i<=19; i++)
        f[u][i]=f[f[u][i-1]][i-1];
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(v==fa)
            continue;
        deep[v]=deep[u]+1;
        dfs(v,u);
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    for (int i=19; i>=0; i--)
    {
         if(deep[x]-(1LL<<i)>=deep[y]) x=f[x][i];
    }
    if(x==y) return x;
    for(int i=19; i>=0; i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
struct T
{
    int l,r,mid;
    int maxx,minn;
} Tree[maxn<<2];
void push_up(int rt)
{
    Tree[rt].maxx=max(Tree[rt<<1].maxx,Tree[rt<<1|1].maxx);
    Tree[rt].minn=min(Tree[rt<<1].minn,Tree[rt<<1|1].minn);
}
void build(int rt,int l,int r)
{
    Tree[rt].l=l;
    Tree[rt].r=r;
    Tree[rt].maxx=-1;
    Tree[rt].minn=1000000;
    if(l==r)
    {
        Tree[rt].maxx=in[l];
        Tree[rt].minn=in[l];
        return ;
    }
    int mid=Tree[rt].mid=l+r>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    push_up(rt);
}
int ans_max,ans_min;
void query(int rt,int l,int r)
{
    if(l>r) return ;
    if(Tree[rt].l>r||Tree[rt].r<l) return ;
    if(Tree[rt].l>=l&&Tree[rt].r<=r)
    {
        ans_max=max(ans_max,Tree[rt].maxx);
        ans_min=min(ans_min,Tree[rt].minn);
        return;
    }
    if(l<=Tree[rt].mid) query(rt<<1,l,r);
    if(r>Tree[rt].mid)  query(rt<<1|1,l,r);
    return ;
}
int main()
{
    int n,q,x,y;
    scanf("%d%d",&n,&q);
    for(int i=2; i<=n; i++)
    {
        scanf("%d",&x);
        G[x].push_back(i);
        G[i].push_back(x);
    }
    dfs(1,0);
    build(1,1,n);
    while(q--)
    {
        scanf("%d%d",&x,&y);
        ans_max=0,ans_min=1000000;
        query(1,x,y);
        int st=mp[ans_max];
        int en=mp[ans_min];
        ans_max=0,ans_min=1000000;
        query(1,x,st-1);query(1,st+1,y);
        int ans1=deep[lca(mp[ans_max],mp[ans_min])];
        ans_max=0,ans_min=1000000;
        query(1,x,en-1);query(1,en+1,y);
        int ans2=deep[lca(mp[ans_max],mp[ans_min])];
        if(ans1>ans2) printf("%d %d\n",st,ans1);
        else printf("%d %d\n",en,ans2);
    }
    return 0;
}

第三种做法

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
#define dbg(x) cout<<#x<<" = "<<x<<endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
const int maxn = 1e5+10;
vector<int> G[maxn];
int deep[maxn];
int f[maxn][20];
int in[maxn],mp[maxn],time;
void dfs(int u,int fa)
{
    f[u][0]=fa;
    in[u]=++time;
    mp[time]=u;
    for(int i=1; i<=19; i++)
        f[u][i]=f[f[u][i-1]][i-1];
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(v==fa)
            continue;
        deep[v]=deep[u]+1;
        dfs(v,u);
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    for (int i=19; i>=0; i--)
    {
         if(deep[x]-(1LL<<i)>=deep[y]) x=f[x][i];
    }
    if(x==y) return x;
    for(int i=19; i>=0; i--)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
struct T
{
    int l,r,mid;
    int maxx[2],minn[2];
} Tree[maxn<<2];
void push_up(int rt)
{
    vector<int> t;
    t.push_back(Tree[rt<<1].maxx[0]);
    t.push_back(Tree[rt<<1].maxx[1]);
    t.push_back(Tree[rt<<1|1].maxx[0]);
    t.push_back(Tree[rt<<1|1].maxx[1]);
    sort(t.begin(),t.end());
    Tree[rt].maxx[0]=t[3];
    Tree[rt].maxx[1]=t[2];

    t.clear();
    t.push_back(Tree[rt<<1].minn[0]);
    t.push_back(Tree[rt<<1].minn[1]);
    t.push_back(Tree[rt<<1|1].minn[0]);
    t.push_back(Tree[rt<<1|1].minn[1]);
    sort(t.begin(),t.end());
    Tree[rt].minn[0]=t[0];
    Tree[rt].minn[1]=t[1];
}
void build(int rt,int l,int r)
{
    Tree[rt].l=l;
    Tree[rt].r=r;
    Tree[rt].maxx[0]=-1;
    Tree[rt].maxx[1]=-1;
    Tree[rt].minn[0]=1000000;
    Tree[rt].minn[1]=1000000;
    if(l==r)
    {
        Tree[rt].maxx[0]=in[l];
        Tree[rt].minn[0]=in[l];
        return ;
    }
    int mid=Tree[rt].mid=l+r>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    push_up(rt);
}
vector<int> maxx,minn;
bool cmp(int a,int b)
{
    return a>b;
}
void query(int rt,int l,int r)
{
    if(l>r) return ;
    if(Tree[rt].l>r||Tree[rt].r<l) return ;
    if(Tree[rt].l>=l&&Tree[rt].r<=r)
    {
        maxx.push_back(Tree[rt].maxx[0]);
        maxx.push_back(Tree[rt].maxx[1]);
        minn.push_back(Tree[rt].minn[0]);
        minn.push_back(Tree[rt].minn[1]);
        return;
    }
    if(l<=Tree[rt].mid) query(rt<<1,l,r);
    if(r>Tree[rt].mid)  query(rt<<1|1,l,r);
    return ;
}
int main()
{
    int n,q,x,y;
    scanf("%d%d",&n,&q);
    for(int i=2; i<=n; i++)
    {
        scanf("%d",&x);
        G[x].push_back(i);
        G[i].push_back(x);
    }
    dfs(1,0);
    build(1,1,n);
    while(q--)
    {
        scanf("%d%d",&x,&y);
        maxx.clear();
        minn.clear();
        query(1,x,y);
        sort(maxx.begin(),maxx.end(),cmp);
        sort(minn.begin(),minn.end());
        int ans1=deep[lca(mp[maxx[0]],mp[minn[1]])];
        int ans2=deep[lca(mp[maxx[1]],mp[minn[0]])];
        if(ans1>ans2) printf("%d %d\n",mp[minn[0]],ans1);
        else printf("%d %d\n",mp[maxx[0]],ans2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值