bzoj4876: [Zjoi2017]线段树(树上差分)

这篇博客详细介绍了如何利用线段树解决Zjoi2017中的一道问题,通过将区间[l, r]转化为寻找节点[l-1, l-1]和[r+1, r+1]路径上的特定子节点,然后应用树上差分策略来求解。博主分享了具体的解题思路和代码实现。" 139504731,7990474,GLM-4-9B模型推理实践:OpenAI接口与Requests调用,"['自然语言处理', '人工智能', '模型推理', '接口调用', 'GLM模型']

传送门
对于这种广义线段树的题,想要定位区间 [ l , r ] [l,r] [l,r]可以转化成定位节点 [ l − 1 , l − 1 ] , [ r + 1 , r + 1 ] [l-1,l-1],[r+1,r+1] [l1,l1],[r+1,r+1]路径上的一些儿子节点。
l c a = t lca=t lca=t
那么路径 [ l − 1 , l − 1 ] → t [l-1,l-1]\rightarrow t [l1,l1]t的右儿子和路径 [ r + 1 , r + 1 ] → t [r+1,r+1]\rightarrow t [r+1,r+1]t的左儿子就是被定位到的区间。
于是就可以树上差分了。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
    int ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
typedef long long ll;
const int N=4e5+5;
int Mid[N],n;
vector<int>e[N];
namespace sgt{
    int son[N][2],id[N],tot=0,cnt=0,siz[N],fa[N],dep[N],top[N],hson[N];
    ll disl[N],disr[N],dissl[N],dissr[N],sizl[N],sizr[N];
    int build(int l,int r){
        int p=++tot;
        if(l==r)return id[l]=p;
        int mid=Mid[++cnt];
        return son[p][0]=build(l,mid),son[p][1]=build(mid+1,r),p;
    }
    void dfs1(int p){
        siz[p]=1;
        for(ri i=0,v;i<2;++i){
            if(!(v=son[p][i]))continue;
            fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
            if(siz[v]>siz[hson[p]])hson[p]=v;
        }
    }
    void dfs2(int p,int tp){
        top[p]=tp;
        if(!hson[p])return;
        dfs2(hson[p],tp);
        for(ri i=0,v;i<2;++i)if((v=son[p][i])>0&&v!=hson[p])dfs2(v,v);
    }
    void dfs(int p){
        dissl[p]+=dissl[fa[p]];
        dissr[p]+=dissr[fa[p]];
        disl[p]+=disl[fa[p]],sizl[p]+=sizl[fa[p]];
        disr[p]+=disr[fa[p]],sizr[p]+=sizr[fa[p]];
        if(son[p][1]){
            disl[p]+=dep[son[p][1]],disl[son[p][1]]-=dep[son[p][1]];
            dissl[p]+=dep[p],dissl[son[p][1]]-=dep[p];
            ++sizl[p],--sizl[son[p][1]];
        }
        if(son[p][0]){
            disr[p]+=dep[son[p][0]],disr[son[p][0]]-=dep[son[p][0]];
            dissr[p]+=dep[p],dissr[son[p][0]]-=dep[p];
            ++sizr[p],--sizr[son[p][0]];
        }
        if(son[p][0])dfs(son[p][0]);
        if(son[p][1])dfs(son[p][1]);
    }
    inline int lca(int x,int y){
        while(top[x]^top[y]){
            if(dep[top[x]]<dep[top[y]])swap(x,y);
            x=fa[top[x]];
        }
        return dep[x]<dep[y]?x:y;
    }
    inline void init(){
        build(1,n);
        int p1=++tot,p2=++tot,p3=++tot,rt=++tot;
        id[0]=p1;
        son[rt][0]=p1,son[rt][1]=p2;
        id[n+1]=p3;
        son[p2][0]=1,son[p2][1]=p3;
        dfs1(rt),dfs2(rt,rt);
        dfs(rt);
    }
    inline ll calc1(int p,int x){
        ll ret=disl[x]+sizl[x]*dep[p];
        int t=lca(p,x);
        ret-=(sizl[x]-sizl[t])*dep[t]*2;
        if(son[t][1])if(lca(son[t][1],p)==son[t][1]&&lca(son[t][1],x)!=son[t][1])ret-=2;
        ret-=dissl[t]*2;
        return ret;
    }
    inline ll calc2(int p,int x){
        ll ret=disr[x]+sizr[x]*dep[p];
        int t=lca(p,x);
        ret-=(sizr[x]-sizr[t])*dep[t]*2;
        if(son[t][0])if(lca(son[t][0],p)==son[t][0]&&lca(son[t][0],x)!=son[t][0])ret-=2;
        ret-=dissr[t]*2;
        return ret;
    }
    inline void solve(int p,int l,int r){
        int t=lca((l=id[l-1]),(r=id[r+1]));
        cout<<calc1(p,l)-calc1(p,t)+calc2(p,r)-calc2(p,t)<<'\n';
    }
}
int main(){
    n=read();
    for(ri i=1;i<n;++i)Mid[i]=read();
    sgt::init();
    for(ri u,l,r,tt=read();tt;--tt){
        u=read(),l=read(),r=read();
        sgt::solve(u,l,r);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值