问题描述
一棵有N个节点的完全二叉树,问有多少种子树所包含的节点数量不同。
输入描述
输入有多组数据,不超过1000组.
每组数据输入一行包含一个整数N.(1≤N≤1018)(1\leq N\leq {10}^{18})
输出描述
对于每组数据输出一行,表示不同节点数的子树有多少种.
输入样例
5 6 7 8
输出样例
3 4 3 5 这道题考到的是递推,因为是完全二叉树,所以总有一边的子树是满子树,而满子树的节点数不同的个数就是层数,所以可以每次去掉根节点, 然后减去满的那边,并将满子树的所有个数可能加进集合,统计个数就行了。#include <stdio.h> #include <set> #define LL long long const int maxn = 65; //2^65才能超过10^18 LL sum[maxn]; int main ( ) { LL n; int ans; std :: set < LL > vis; sum[0] = 1; for ( int i = 1; i < maxn; i ++ ) sum[i] = sum[i-1]*2; //把2的次方保存到数组,用左移时间会超限 while ( ~ scanf ( "%I64d", &n ) ) { vis.clear ( ); vis.insert ( n ); ans = 1; n --; //每次都去掉根节点 while ( n > 0 ) { int i; LL t = n; for ( i = 1; ; i ++ ) { if ( t-sum[i] < 0 ) break ; t = t-sum[i]; //能搜到的最大层数 } int m; if ( t >= sum[i-1] ) //大于等于上一层的一半时,往右 m = i; else //否则去掉右边部分,往左 m = i-1; LL s = 0; for ( int j = 0; j < m; j ++ ) { s = s+sum[j]; //统计删除层的每种个数 if ( vis.count ( s ) ) continue ; ans ++; vis.insert ( s ); } n = n-s; //去掉一边子树 if ( n && ! vis.count ( n ) ) //n!=0需要考虑 { ans ++; vis.insert ( n ); } n --; //去掉根节点 } printf ( "%d\n", ans ); } return 0; }