51nod 1297 管理二叉树

一个初始为空的二叉搜索树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个数,对应添加当前元素后,每对节点之间的距离之和。

用set维护一下空的左右孩子位置和已插入的点可以O(nlogn)建树

然后就每加入 √(n) 个点重新树形dp一次,每加入一个点时和之前已加入但未dp的点间求距离,并计算和已dp的点对答案的贡献。

总复杂度O(n√(n)logn)

读入优化t了4个点,读入+输出优化t了1个点,fread+fwrite才a。。

#include<stdio.h>
#include<set>
#include<cmath>
const int N=100007;
char buf[2000003],*ptr=buf,rbuf[1000003],*rptr=rbuf-1;
inline int _(){
    int x=0,c=*++rptr;
    while(c<48)c=*++rptr;
    while(c>47)x=x*10+c-48,c=*++rptr;
    return x;
}
inline void _(long long x){
    static int stk[32],stp=0;
    if(!x)stk[stp++]=0;
    while(x)stk[stp++]=x%10,x/=10;
    while(stp)*ptr++=stk[--stp]+48;
    *ptr++=10;
}
int n,pv=0;
std::set<int>le,re,in;
int ch[N][2],xs[N],dep[N],fa[N],sz[N],pf[N],top[N];
long long F1[N],F2[N],ans=0,ANS[N];
int _sz[N],h[N],tp[N];
inline int dis(int x,int y){
    int a=top[x],b=top[y],s=dep[x]+dep[y];
    while(a!=b){
        if(dep[a]<dep[b])y=fa[b],b=top[y];
        else x=fa[a],a=top[x];
    }
    s-=dep[dep[x]<dep[y]?x:y]<<1;
    return s;
}
void rebuild(){
    ans=0;
    for(int i=pv-1;~i;--i){
        int w=xs[i],lc=ch[w][0],rc=ch[w][1];
        h[w]=0;
        _sz[w]=1+_sz[lc]+_sz[rc];
        F1[w]=_sz[w]-1+F1[lc]+F1[rc];
    }
    for(int i=0;i<pv;++i){
        int w=xs[i],lc=ch[w][0],rc=ch[w][1],s=_sz[rc]-_sz[lc];
        F2[lc]=pv+s+F2[w]+F1[rc];
        F2[rc]=pv-s+F2[w]+F1[lc];
        F1[w]+=F2[w];
        ans+=F1[w];
    }
    ans>>=1;
}
int main(){
    rbuf[fread(rbuf,1,1000000,stdin)]=0;
    n=_();
    xs[0]=_();
    le.insert(xs[0]);
    re.insert(xs[0]);
    in.insert(xs[0]);
    for(int i=1;i<n;++i){
        int x=xs[i]=_();
        in.insert(x);
        auto it=le.upper_bound(x);
        if(it!=le.end()){
            auto it2=in.upper_bound(x);
            if(it2!=in.end()&&*it2==*it){
                ch[fa[x]=*it][0]=x;
                le.erase(it);
                le.insert(x),re.insert(x);
                continue;
            }
        }
        it=re.upper_bound(x);
        --it;
        ch[fa[x]=*it][1]=x;
        re.erase(it);
        le.insert(x),re.insert(x);
    }
    for(int i=0;i<n;++i){
        int w=xs[i];
        dep[w]=dep[fa[w]]+1;
    }
    for(int i=n-1;~i;--i){
        int w=xs[i];
        int sl=sz[ch[w][0]],sr=sz[ch[w][1]];
        sz[w]=1+sl+sr;
        pf[w]=ch[w][sl<sr];
    }
    for(int i=0;i<n;i++){
        int w=xs[i];
        if(!top[w])for(int u=w;u;u=pf[u])top[u]=w;
    }
    int B=std::sqrt(n)/1.2+1;
    for(int i=0;i<n;++i){
        int w=xs[i],f=fa[w];
        tp[w]=h[f]?tp[f]:f;
        h[w]=h[f]+1;
        ans+=h[w]*pv+F1[tp[w]];
        for(int j=pv;j<i;j++)ans+=dis(w,xs[j]);
        ANS[i]=ans;
        if(i%B==B-1){
            pv=i+1;
            rebuild();
        }
    }
    for(int i=0;i<n;++i)_(ANS[i]);
    fwrite(buf,1,ptr-buf,stdout);
    return 0;
}
时间: 2024-11-08 03:59:13

51nod 1297 管理二叉树的相关文章

51 nod 1297 管理二叉树

原题链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1297 先是暴力加优化T了最后两个点…… 我还是来想想正解吧. 先写棵线段树把二叉搜索树建出来,然后在树上做下点分治就好了. #include<cstdio> #include<cstring> #include<algorithm> #define lp (p<<1) #define rp ((p<<1)|1

【数据结构】 二叉树

二叉树概念 在计算机科学中,二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree).二叉树常被用于实现二叉查找树和二叉堆. 二 叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒.二叉树的第i层至多有2^{i-1}个结点:深度为k 的二叉树至多有2^k-1个结点:对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_

51nod 1832 先序遍历与后序遍历【二叉树+高精度】

题目链接:51nod 1832 先序遍历与后序遍历 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 对于给定的一个二叉树的先序遍历和后序遍历,输出有多少种满足条件的二叉树.两棵二叉树不同当且仅当对于某个x,x的左儿子编号不同或x的右儿子编号不同. Input 第一行一个正整数n(3<=n<=10000),表示二叉树的节点数,节点从1到n标号. 第二行n个整数a[i](1<=a[i]<=n),表示二叉树的先序遍历. 第三行n个整数b[i](1<

数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树

[本文谢绝转载,原文来自http://990487026.blog.51cto.com] 树 数据结构与算法 3:二叉树,遍历,创建,释放,拷贝,求高度,面试,线索树 二叉树的创建,关系建立 二叉树的创建,关系建立2 三叉链表法 双亲链表: 二叉树的遍历 遍历的分析PPT 计算二叉树中叶子节点的数目:使用全局变量计数器 计算二叉树中叶子节点的数目:不使用全局变量计数器 无论是先序遍历,中序遍历,后序遍历,求叶子的数字都不变;因为本质都是一样的,任何一个节点都会遍历3趟 求二叉树的高度 二叉树的拷

linux内核分析之内存管理

1.struct page 1 /* Each physical page in the system has a struct page associated with 2 * it to keep track of whatever it is we are using the page for at the 3 * moment. Note that we have no way to track which tasks are using 4 * a page, though if it

[自制简单操作系统] 3、内存管理和窗口叠加

1.本次主要进展 >_<" 这次主要学习了系统内存管理和窗口叠加~由于上两篇都做了详细的框架说明和介绍,这里直接上代码! 2.文件及函数构成 >_<" 这里和第二篇相比,把鼠标和键盘的相关函数独立出来放进各自相应的文件中,并主要在内存管理和窗口叠加进行探索,同时还有部分代码整理~ 1 /* In this file, not only have the defination of the function, but also 2 hava the descrip

堆、二叉树的应用

一.本次实验环境: 腾讯云虚拟主机centos7.2上配置pyenv多版本python管理器,并安装交互式web编辑器jupyter,python版本为3.5.2,利用xshell远程ssh连接腾讯云主机,操作简易.方便. 二.对堆的简单认识: 1.堆是局部有序,且是一棵高度为O(lgN)的完全二叉树,其基本操作至多与树的高度成正比    2.堆排序:非稳定排序,最好和平均时间复杂度和快速排序相当,但是最坏情况下的时间复杂度要优于快速排序,由于他对元素的操作通常在N和N之间进行,所以对于大的序列

java学习之二叉树的实现

二叉树是一种数据结构,每个节点都有两个子节点. 二叉树的遍历有三种方式, 先序遍历是 根节点,左子树,右子树: 中序遍历是 左子树,根节点,右子树: 后序遍历是 左子树,右子树,根节点: java实现: 1 package com.gh.Binary; 2 3 /** 4 * 二叉树的实现 5 * 6 * @author ganhang 7 * 8 */ 9 public class BinaryTreeDemo { 10 public static void main(String[] arg

深入探索spring技术内幕(二): 剖析spring管理Bean的原理与配置

求二叉树的宽度和深度 给定一个二叉树,获取该二叉树的宽度和深度. 例如输入 a / \ b c / \ / \ d e f g 返回3. 详细描述: 接口说明 原型: int GetBiNodeInfo(BiNode &head, unsigned int *pulWidth, unsigned int *pulHeight) 输入参数: head 需要获取深度的二叉树头结点 输出参数(指针指向的内存区域保证有效): pulWidth 宽度 pulHeight 高度 返回值: 0 成功 1 失败