牛客网 二叉树 Tree IV

链接:https://ac.nowcoder.com/acm/contest/9004/B
来源:牛客网

题目描述

给出一棵有n个结点的标准的完全二叉树(即,若父结点编号为x,则它的两个子结点的编号分别为x×2x \times 2x×2和x×2+1x \times 2 + 1x×2+1),定义每个结点的价值为xdepxxdep_xxdepx​,即每个点的编号乘以每个点的深度(深度即为从根结点到这一结点最短路径经过的点的个数,定义根结点1的深度为1)。

请你求解这棵树中每个结点的价值和(由于答案可能会很大,请你对998244353取模),即∑i=1nidepi mod 998244353\sum\limits_{i=1}^{n}idep_i \ mod\ 998244353i=1∑n​idepi​ mod 998244353。

完全二叉树:若设二叉树的深度为k,除第 k 层外,其它各层 (1~k-1) 的结点数都达到最大个数,第 k 层所有的结点都连续集中在最左边。

例如(图为一棵标准的完全二叉树):

测试数据:输入5,输出38

其计算公式为:1 * 1 + (2 + 3) * 2 + (4 + 5) * 3 = 38 (此式乘号前面为结点编号的和,后面为这些节点对应的深度)

数据满足:1≤n≤1091\leq n\leq 10^91≤n≤109

 

解题思路
首先确定以下几点:
1、每一个结点的价值等于结点编号乘以结点深度
2、整个二叉树的价值等于所有结点的价值之和
3、二叉树每一层所有结点的深度相同,也就是结点层数

二叉树有以下特点:
1、每层所有结点编号构成一个等差数列,其项数就是当前层的结点总个数,同时也是当前层的首项
2、所有层的第一个编号,也就是每层的首项构成一个等比数列设当前结点的深度,也即所在层数为k,
每层结点编号连续,构成一个递增的等差数列,其公差为1,首项为2^(k-1),同时也是前一层首项的2倍,最多有2^(k-1)项,
因此,可根据等差数列求和公式直接求得当前层所有结点的价值和

# 最初解答:通过40%
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
# 
# @param n long长整型 表示标准完全二叉树的结点个数
# @return long长整型
#
class Solution:
    def tree4(self , n ):
        # write code here
        value = 0
        deep = 0
        m = n
        while n>0:
            n = n//2
            deep +=1
        for i in range(1, deep + 1):
            if i!=deep:
                value += sum(range(2**(i-1), 2**i))*i
            else:
                value += sum(range(2**(i-1), m+1))*i
        return value
        
while True:
    try:
        n = int(input())
        print(Solution().tree4(n))
    except:
        break
        
# 改进1:加入对998244353取模,深度逐层增加
# 通过80%,提示算法复杂的太大

#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
# 
# @param n long长整型 表示标准完全二叉树的结点个数
# @return long长整型
#
class Solution:
    def tree4(self , n ):
        # write code here
        value = 0
        deep = 1
        while 2**deep<=n:
            value += sum(range(2**(deep-1), 2**deep))*deep
            deep += 1
        value += sum(range(2**(deep-1), n+1))*deep
        return value % 998244353
        
while True:
    try:
        n = int(input())
        print(Solution().tree4(n))
    except:
        break
        
# 改进算法2,利用等差数列求和,去掉**运算,使用乘2迭代,但是通过率仍然只有80%
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# @param n long长整型 表示标准完全二叉树的结点个数
# @return long长整型
#
class Solution:
    def tree4(self , n ):
        # write code here
        value = 1
        deep = 2
        a = 2
        while 2*a<=n:
            value += (a*(3*a-1)/2 * deep) % 998244353
            deep += 1
            a = 2*a
        b = n - a + 1
        value += ((a*b+b*(b-1)/2) * deep) % 998244353
        return int(value % 998244353)

while True:
    try:
        n = int(input())
        print(Solution().tree4(n))
    except:
        break
# 改进算法3,将/修改为//,成功AC,因为/运算后是一个小数,而//返回的是一个整数,其存储格式不一致,
# 导致精度有所丢失
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# @param n long长整型 表示标准完全二叉树的结点个数
# @return long长整型
#
class Solution:
    def tree4(self , n ):
        # write code here
        value,deep,a = 1,2,2
        while 2*a<=n:
            value += ((a*(3*a-1)//2) * deep)
            deep += 1
            a = 2*a
        b = n - a + 1
        value += ((a*b+b*(b-1)//2) * deep)
        return value % 998244353

while True:
    try:
        n = int(input())
        print(Solution().tree4(n))
    except:
        break

第k层所有结点价值和公式为:

 

与前一层 的递推公式为:

 

设第k层共有β个结点,则第k层的价值和为:

 若第k层结点全部包括,则:

 若第k层结点未全部包括,即最后一层,则:

 然后对所有项求和即可,每求一次,层数(深度)k加1,相应的首项乘2,乘2可用左移1位解决,整除2可用右移解决

# 改进算法4,大幅度减少代码量
# 每一层的结点编号都是一个递增的等差数列,可以利用求和公式
# 程序中的v为价值和,k为深度(也就是层数),a为每一层的首项,b为项数
class Solution:
    def tree4(self , n ):
        v,k,a = 1,2,2
        while a <= n:
            b = a if (a<<1)<=n else n-a+1
            v,k,a = v+(a*b+(b*(b-1)>>1))*k,k+1,a<<1
        return v % 998244353
while True:
    try:
        n = int(input())
        print(Solution().tree4(n))
    except:
        break

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐观的lishan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值