链接: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∑nidepi 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