问题描述:
已知:存在一个满M叉树,该树任意结点的值是其父结点值的1/(M+1),叶节点的值为1。.已知根结点的值(记为H)和叶结点的个数(记为N)
求:非叶结点的个数和所有结点值之和
输入:若干case,每个case独立一行,包含两个整数分别代表根结点的值和叶结点的个数,0 0表示输入结束
输出:每组case对应的非叶结点的个数和所有结点值之和
Sample Input:
216 125
5764801 1679616
0 0
Sample Output:
31 671
335923 30275911
思路:
记给定树的深度为D(从0开始),根据问题描述易知如下特性:
- M^D = N
- (M+1)^D = H
因此,本题的唯一难点在于通过试任意N次方根求D和M。本题有两个陷阱:首先,采用试N次方的方法会导致TLE,因为对底数和指数都要试验,非常耗时;其次,
直接使用类似于pow(N, 1/D)所得结果存在误差,不能直接使用
代码:
#include <stdio.h>
#include <math.h>
int main(){
unsigned long long h, n, ans1, ans2, m; //n为叶结点个数,m为度
int odd;
double d, t1, t2; //d表示深度
while(1){
scanf("%lld%lld", &h, &n);
if(h == 0 && n == 0) break;
else if(n == 1 && h == 1) printf("0 1\n");
else{
for(d = 1; d <= 32; d++){
t1 = pow(h, 1.0/d);
t2 = pow(n, 1.0/d);
if(fabs(t1-t2-1)<1e-14) //关键,使用pow()求任意N次方根存在误差,这里的1e-14即允许的误差范围
break;
}
if(n != 1){
m = t2+0.1; //关键,开方的结果t2(浮点数)转换为整型,需要加一个小数0.1,而不能使用ceil()或floor()
ans1 = (n-1)/(m-1); //推导所得公式
ans2 = num*(h-n)+h;
}
else{
m = 1;
ans1 = d;
ans2 = 2*h-n;
}
printf("%lld %lld\n", ans1, ans2);
}
}
return 0;
}