【51nod1297】【管理二叉树】【点分治】

本文介绍了一种利用二叉搜索树和重心树解决特定问题的方法。通过对输入序列进行处理并构建相应的树结构,文章详细阐述了如何计算树上每对节点间距离之和的高效算法。

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

题目大意

一个初始为空的二叉搜索树T,以及1到N的一个排列P: {a1, a2, …, aN}。我们向这个二叉搜索树T添加这些数,从a1开始, 接下来是 a2, …, 以aN结束。在每一个添加操作后,输出T上每对节点之间的距离之和。

解题思路

建树暴力会超时,观察可知一个点只会挂在比它小的点中最大的点的右边或比它大的点中最小的点的左边,并且有且只有一个位置是空的,我们可以用set来求这个。
接下来就是建重心树(点分树),找一个重心然后剖开树,子节点是剖开的树的重心。接下来求的是一个点和比它先加入的点的距离和,可以考虑在重心树上求答案,每个点保存所有子节点中加入过的点到它的距离和,到它父亲的距离和,还有所有子节点中加入过点的数,即可求出答案。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define Min(a,b) ((a<b)?a:b)
#define Max(a,b) ((a>b)?a:b)
#define Fo(i,j,k) for(int i=j;i<=k;i++)
#define Fd(i,j,k) for(int i=j;i>=k;i--)
#define For(i,j) for(int i=Begin[j];i;i=Next[i])
using namespace std;
int const Mxn=1e5,Mxm=2*1e5;
int N,Gra,Mxb,Size[Mxn+9],Fa[Mxn+9],Vis[Mxn+9],Num[Mxn+9],Dep[Mxn+9],Son[Mxn+9][2],Fat[Mxn+9][18],Begin[Mxn+9],To[Mxm+9],
    Next[Mxm+9];LL F[Mxn+9],G[Mxn+9],Ans;
struct Rec{
    int Pos,Val;
    Rec(int X,int Y){Pos=X;Val=Y;}
    friend bool operator<(Rec X,Rec Y){return X.Val<Y.Val;}
};
multiset<Rec>S;
void Insert(int U,int V){
    To[++Gra]=V;
    Next[Gra]=Begin[U];
    Begin[U]=Gra;
}
int Cmid(int Now,int Tsize){
    Vis[Now]=1;
    For(i,Now)if(!Vis[To[i]]){
        int Tmp=Cmid(To[i],Tsize);
        if(Tmp){Vis[Now]=0;return Tmp;}
    }
    Vis[Now]=0;
    if(Size[Now]>=(Tsize>>1))return Now;
    return 0;
}
void Csize(int Now){
    Vis[Now]=1;Size[Now]=1;
    For(i,Now)if(!Vis[To[i]]){
        Csize(To[i]);
        Size[Now]+=Size[To[i]];
    }
    Vis[Now]=0;
}
int Build(int Now){
    Csize(Now);
    Now=Cmid(Now,Size[Now]);
    Vis[Now]=1;
    For(i,Now)if(!Vis[To[i]]){
        int Tmp=Build(To[i]);
        Fa[Tmp]=Now;
    }
    return Now;
}
int Cdis(int U,int V){
    int UU=U,VV=V;
    if(Dep[U]<Dep[V])swap(U,V);
    Fd(i,Mxb,0)if(Dep[Fat[U][i]]>=Dep[V])U=Fat[U][i];
    if(U==V)return Dep[UU]+Dep[VV]-2*Dep[U];
    Fd(i,Mxb,0)if(Fat[U][i]!=Fat[V][i])U=Fat[U][i],V=Fat[V][i];
    return Dep[UU]+Dep[VV]-2*Dep[Fat[U][0]];
}
void Dfs(int Now,int Pre){
    Dep[Now]=Dep[Pre]+1;Fat[Now][0]=Pre;
    For(i,Now)if(To[i]!=Pre)Dfs(To[i],Now);
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&N);
    int A;scanf("%d",&A);
    S.insert(Rec(1,A));
    S.insert(Rec(0,0));
    S.insert(Rec(0,N+1));
    Fo(i,2,N){
        scanf("%d",&A);
        S.insert(Rec(i,A));
        Rec Pre=*(--S.find({i,A})),Next=*(++S.find({i,A}));
        if((!Pre.Pos)||(Son[Pre.Pos][1]))
            Son[Next.Pos][0]=i,Insert(Next.Pos,i),Insert(i,Next.Pos);
        else Son[Pre.Pos][1]=i,Insert(Pre.Pos,i),Insert(i,Pre.Pos);
    }
    int Root=Build(1);
    Dfs(1,0);Mxb=log(N)/log(2);
    Fo(j,1,Mxb)Fo(i,1,N)Fat[i][j]=Fat[Fat[i][j-1]][j-1];
    LL Ans=0;
    Fo(i,1,N){
        Ans+=F[i];int Now=i;
        while(Fa[Now])Ans+=F[Fa[Now]]-G[Now]+
            1ll*(Num[Fa[Now]]-Num[Now])*Cdis(i,Fa[Now]),Now=Fa[Now];
        Now=i;
        while(Fa[Now])Num[Now]++,F[Fa[Now]]+=Cdis(i,Fa[Now]),
            G[Now]+=Cdis(i,Fa[Now]),Now=Fa[Now];
        Num[Now]++;
        printf("%lld\n",Ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值