题目大意
给定一个n的排列,用它建出二叉搜索树。输出每次插入后所有节点两两之间距离和。
n≤100000
分析
考虑离线做。
首先把这棵树建出来。建树可以O(n),具体点:可以发现,插入一个值x时,它一定是之前已经插入的数中,和它相邻的两个数中后插入的数值儿子。
比如:4 7 3 1 8 2 6 5。1是3的左儿子,2是1的右儿子,6是8的左儿子。
建出树之后,把排列中的数按顺序放进来。假设知道了前i-1个数的答案,现在要求前i个答案,只要加上i到其它所有节点的距离和。
那么可以用点分治做。具体就不说了。
时间复杂度O(nlogn)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=100005,Log=18;
typedef long long LL;
int n,e[N][3],m,a[N],f1[N],f2[N],cnt[N],son_cnt[N][3],rmq[N*2][Log],tot,la[N],dep[N],fa[N],dfn[N],size[N],ap[N];
int s[N],R,V;
LL dis[N],son_dis[N][3],ans;
bool bz[N];
char c;
int read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
int x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}
int get(int x,int *f)
{
if (f[x]==-1) return x;
return f[x]=get(f[x],f);
}
void init(int x)
{
dep[x]=dep[e[x][0]]+1;
dfn[x]=++m;
rmq[la[x]=tot++][0]=x;
for (int i=1;i<3;i++) if (e[x][i])
{
init(e[x][i]); rmq[la[x]=tot++][0]=x; size[x]+=size[e[x][i]]+1;
}
}
void find(int x,int y)
{
int M=0;
s[x]=1;
for (int i=0;i<3;i++) if (!bz[e[x][i]] && e[x][i]!=y)
{
find(e[x][i],x);
s[x]+=s[e[x][i]];
M=max(M,s[e[x][i]]);
}
M=max(M,m-s[x]);
if (M<V)
{
V=M; R=x;
}
}
int dfs(int x)
{
V=m;
find(x,0);
int r=R,sum=m;
bz[r]=1;
for (int i=0;i<3;i++) if (!bz[e[r][i]])
{
m=(s[e[r][i]]<s[r])?s[e[r][i]]:sum-s[r];
fa[dfs(e[r][i])]=r;
}
return r;
}
int getlca(int x,int y)
{
x=la[x]; y=la[y];
if (x>y) x^=y^=x^=y;
int k=trunc(log(y-x+1)/log(2));
return (dep[rmq[x][k]]<=dep[rmq[y-(1<<k)+1][k]])?rmq[x][k]:rmq[y-(1<<k)+1][k];
}
int main()
{
n=read();
for (int i=1;i<=n;i++)
{
a[i]=read(); ap[a[i]]=i;
}
memset(f1,255,sizeof(f1));
memset(f2,255,sizeof(f2));
for (int i=n;i>1;i--)
{
int x=a[i];
f1[x]=x-1; f2[x]=x+1;
int l=get(x,f1),r=get(x,f2);
if (ap[l]>ap[r])
{
e[x][0]=l; e[l][(l<x)+1]=x;
}else
{
e[x][0]=r; e[r][(r<x)+1]=x;
}
}
init(a[1]);
for (int j=1;j<Log;j++)
for (int i=0;i<=tot-(1<<j);i++)
rmq[i][j]=(dep[rmq[i][j-1]]<=dep[rmq[i+(1<<(j-1))][j-1]])?rmq[i][j-1]:rmq[i+(1<<(j-1))][j-1];
m=n; bz[0]=1;
fa[dfs(a[1])]=0;
for (int i=1;i<=n;i++)
{
ans+=dis[a[i]];
cnt[a[i]]++;
for (int j=fa[a[i]];j;j=fa[j])
{
int t,d=dep[a[i]]+dep[j]-2*dep[getlca(a[i],j)];
if (dfn[a[i]]<dfn[j] || dfn[j]+size[j]<dfn[a[i]]) t=0;
else if (e[j][1]>0 && dfn[a[i]]<=dfn[e[j][1]]+size[e[j][1]]) t=1;else t=2;
ans+=dis[j]-son_dis[j][t]+(LL)(cnt[j]-son_cnt[j][t])*d;
cnt[j]++; son_cnt[j][t]++; dis[j]+=d; son_dis[j][t]+=d;
}
printf("%lld\n",ans);
}
return 0;
}
本文介绍了一种在线性时间内构建二叉搜索树的方法,并通过点分治算法计算节点间距离和。适用于处理大规模数据集,实现高效查询。
430

被折叠的 条评论
为什么被折叠?



