题目大意
一个初始为空的二叉搜索树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;
}