[bzoj 3626][LNOI2014]LCA

本文介绍了一种高效解决有根树中特定区间节点与另一固定节点间最近公共祖先深度之和的算法。通过预处理和使用线段树或二进制索引树,该算法能在O(log n)时间内完成单次查询,适用于大规模数据集。

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

传送门

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。

一个点的深度定义为这个节点到根的距离+1。

设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。

有q次询问,每次询问给出l r z,求∑l≤i≤rdep[LCA(i,z)] 。

(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

Solution

首先把询问拆成两个部分

可以把每个点的贡献看成是它到根节点路径上的累加

所以每次加点的时候,就把它到根的路径上全部+1

按顺序加点,询问时直接对它到根的路径求和就可以了


Code 

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

#define MN 50005

struct edge{int to,nex;}e[MN];int en,hr[MN];
inline void ins(int f,int t){e[++en]=(edge){t,hr[f]};hr[f]=en;}

int fa[MN],dep[MN],mx[MN],siz[MN],top[MN],dfn[MN],dind;
void dfs1(int x=1)
{
    dep[x]=dep[fa[x]]+(siz[x]=1);register int i;
    for(i=hr[x];i;i=e[i].nex) dfs1(e[i].to),siz[x]+=siz[e[i].to],siz[e[i].to]>siz[mx[x]]?mx[x]=e[i].to:0;
}
void dfs2(int x=1,int tp=1)
{
    top[x]=tp;dfn[x]=++dind;if(mx[x]) dfs2(mx[x],tp); register int i;
    for(i=hr[x];i;i=e[i].nex) if(mx[x]^e[i].to) dfs2(e[i].to,e[i].to);
}

class BIT
{
    #define NM 50005
    #define reg register
    #define lb(x) (x&(-x))
    private:
        ll t1[NM],t2[NM],N;
    public:
        BIT(int _n):N(_n){memset(t1,0,sizeof t1);memset(t2,0,sizeof t2);}
        inline void CC(int p,int v){for(reg int x=p;x<=N;x+=lb(x))t1[x]+=v,t2[x]+=v*p*1ll;}
        inline void C(int l,int r,int x){CC(l,x);CC(r+1,-x);}
        inline ll GG(int p){ll r=0;for(reg int x=p;x;x-=lb(x))r+=(p+1)*t1[x]-t2[x];return r;}
        inline ll G(int l,int r){return GG(r)-GG(l-1);}
    #undef NM
    #undef lb
    #undef reg
};
struct ques{
    int z,id,a,val;
    bool operator<(const ques&o) const{return z<o.z;}
}q[MN<<1];
int n,Q,ans[MN];

inline int INS_Query(int x,bool p)
{
    static BIT T(n);
    if(p)
    {
        while(x)
        {
            T.C(dfn[top[x]],dfn[x],1);
            x=fa[top[x]];
        }
        return 0;
    }
    else
    {
        int r=0;
        while(x)
        {
            r+=T.G(dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
        return r;
    }
}


int main()
{
//  freopen("tree.in","r",stdin);
//  freopen("tree.out","w",stdout);
    n=read();Q=read();
    register int i,pos;
    for(i=2;i<=n;++i) fa[i]=read()+1,ins(fa[i],i);
    for(i=1;i<=Q;++i)
    {
        q[i*2-1].z=read(),q[i*2].z=read()+1;
        q[i*2-1].a=q[i*2].a=read()+1;
        q[i*2].id=q[i*2-1].id=i;
        q[i*2].val=1;q[i*2-1].val=-1;
    }
    std::sort(q+1,q+Q*2+1);dfs1();dfs2();
    for(pos=0,i=1;i<=Q*2;++i)
    {
        for(;pos<q[i].z;pos++) INS_Query(pos+1,1);
        ans[q[i].id]+=INS_Query(q[i].a,0)*q[i].val;
    }
    for(i=1;i<=Q;++i) printf("%d\n",ans[i]%201314);
    return 0;
}



Blog来自PaperCloud,未经允许,请勿转载,TKS!

转载于:https://www.cnblogs.com/PaperCloud/p/10206971.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值