您的位置:首页 > 其它

BC 61 Subtrees

2015-11-02 08:53 218 查看
问题描述
一棵有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;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: