51nod 1297 管理二叉树

本文解析了一道关于管理二叉树的算法题,通过动态点分治的方法,在每次添加节点后计算二叉树中所有节点间的距离之和。

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

题目来源:  HackerRank
基准时间限制:3 秒 空间限制:131072 KB 分值: 640  难度:8级算法题
 收藏
 关注
一个初始为空的二叉搜索树T,以及1到N的一个排列P: {a1, a2, ..., aN}。我们向这个二叉搜索树T添加这些数,从a1开始, 接下来是 a2, ..., 以aN结束。在每一个添加操作后,输出T上每对节点之间的距离之和。
例如:4 7 3 1 8 2 6 5。最终的二叉树为:

       4
     /   \
    3      7   
  /      /   \
 1      6     8
  \    /
   2  5

节点两两之间的距离和 = 6+5+5+4+3+2+1+5+4+4+3+2+1+4+3+3+2+1+3+2+2+1+2+1+1+2+1+3 = 76
Input
第1行:1个数N。(1 <= N <= 100000)
第2 - N + 1行:每行1个数,对应排列的元素。(1 <= ai <= N)
Output
输出共N行,每行1个数,对应添加当前元素后,每对节点之间的距离之和。
Input示例
8
4
7
3
1
8
2
6
5
Output示例
0
1
4
10
20
35
52
76
李陶冶  (题目提供者)



【分析】

动态点分治

预处理出来各个节点与各层分治中心的距离等信息,按时间顺序逐个插入点统计答案。




【代码】

//51nod 1297 管理二叉树 
#include<bits/stdc++.h>
#define inf 1e6
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int mxn=150005;
set <int> s;
bool vis[mxn];
ll ans,tmp;
int n,m,T,cnt,tot,root;
int head[mxn],ch[mxn][2];
ll ans1[mxn],ans2[mxn],num[mxn];
int a[mxn],dep[mxn],pre[mxn],nxt[mxn];
int anc[mxn][22],dis[mxn][22],mx[mxn],sz[mxn];
struct edge {int to,next;} f[mxn<<1];
inline void add(int u,int v)
{
	f[++cnt].to=v,f[cnt].next=head[u],head[u]=cnt;
	f[++cnt].to=u,f[cnt].next=head[v],head[v]=cnt;
}
inline void dfs_size(int u,int fa)
{
	sz[u]=1,mx[u]=0;
	for(int i=head[u];i;i=f[i].next)
	{
		int v=f[i].to;
		if(v==fa || vis[v]) continue;
		dfs_size(v,u);
		sz[u]+=sz[v];
		mx[u]=max(mx[u],sz[v]);
	}
	mx[u]=max(mx[u],tot-sz[u]);
	if(mx[u]<mx[root]) root=u;
}
inline void dfs_dis(int u,int fa,int Anc,ll Dis)
{
	dep[u]++;
	anc[u][dep[u]]=Anc;
	dis[u][dep[u]]=Dis;
	for(int i=head[u];i;i=f[i].next)
	{
		int v=f[i].to;
		if(v==fa || vis[v]) continue;
		dfs_dis(v,u,Anc,Dis+1);
	}
}
inline void dfs(int u)
{
	mx[root=0]=inf;
	dfs_size(u,0);
	dfs_dis(root,root,root,0);
	dep[root]--,vis[root]=1;
	for(int i=head[root];i;i=f[i].next)
	{
		int v=f[i].to;
		if(vis[v]) continue;
		dfs_size(v,0);
	    tot=sz[v],dfs(v);
	}
}
inline void solve(int u)
{
	tmp=ans1[u];
	for(int i=dep[u];i>1;i--)
	{
		ll Dis=dis[u][i-1];
		tmp+=ans1[anc[u][i-1]]-ans2[anc[u][i]];
		tmp+=Dis*(num[anc[u][i-1]]-num[anc[u][i]]);
	}
	ans+=tmp,num[u]++;
	for(int i=dep[u];i>1;i--)
	{
		ll Dis=dis[u][i-1];
		ans1[anc[u][i-1]]+=Dis;
		ans2[anc[u][i]]+=Dis;
		num[anc[u][i-1]]++;
	}
	printf("%lld\n",ans);
}
inline void build()
{
	set <int>::iterator pr,nt;
	s.insert(a[1]);
	fo(i,2,n)
	{
		s.insert(a[i]);
		pr=nt=s.lower_bound(a[i]);
		if(nt!=s.end())
		{
			nt++;
			if(nt!=s.end())
			  nxt[a[i]]=(*nt);
		}
		if(pr!=s.begin())
		  pr--,pre[a[i]]=(*pr);
	}
	fo(i,2,n)
	{
		int u=a[i];
		if(!pre[u] || ch[pre[u]][1])
		{
			ch[nxt[u]][0]=u;
			add(nxt[u],u);
//			printf("connect=[%d,%d]\n",nxt[u],u);
			continue;
		}
		if(!nxt[u] || ch[nxt[u]][0])
		{
			ch[pre[u]][1]=u;
			add(pre[u],u);
//			printf("connect=[%d,%d]\n",pre[u],u);
		}
	}
}
int main()
{
	scanf("%d",&n);
	fo(i,1,n)
	  scanf("%d",&a[i]);
	build();
	tot=n,dfs(1);
	fo(i,1,n) anc[i][++dep[i]]=i;
	fo(i,1,n) solve(a[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值