2022 Jiangsu Collegiate Programming Contest - J. Balanced Tree

本文介绍了一种高效算法,用于计算具有特定平衡条件的二叉树数量。通过递推公式结合对数运算,实现了对大规模数据的有效处理,特别适用于求解节点数量巨大时的平衡二叉树组合数目。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

L. Collecting Diamonds

题目描述

A binary tree T is called super balanced if T is empty or satisfies the following three conditions at the same time:

The left subtree of T is super balanced.
The right subtree of T is super balanced.
The number of nodes of the left subtree and the right subtree differs at most 1.
Please calculate the number of super balanced trees consisting of n nodes. Since the answer can be very large, just output the answer modulo 2^64.

输入描述

The first line contains a single integer T (1≤T≤106), indicating the number of test cases.

Each test case contains a single integer n (0≤n<264), indicating the number of nodes in the tree.

输出描述

For each test case, output an integer in a single line indicating the number of super balanced trees consisting of n nodes.

输入样例1:

2
2
3

输出样例1:

2
1

题意

求构造一棵使每个节点的左右子树的 size 之差小于等于 1 的方案数
对 2^64 取模

思路

设 f[x] 为有 x 个节点的方案树
特殊的 f[0]=1
i)若 x为偶数: f [ x ] = f [ x / 2 ] ∗ f [ x / 2 − 1 ] ∗ 2 f[x]=f[x/2]*f[x/2-1]*2 f[x]=f[x/2]f[x/21]2
ii)若 x为奇数: f [ x ] = f [ x / 2 ] 2 f[x]=f[x/2]^2 f[x]=f[x/2]2
观察柿子可知都是以2的幂次在进行计算
故我们设 g [ x ] = l o g 2 ( f [ x ] ) g[x]=log_2(f[x]) g[x]=log2(f[x])
i)若 x为偶数: g [ x ] = g [ x / 2 ] + g [ x / 2 − 1 ] + 1 g[x]=g[x/2]+g[x/2-1]+1 g[x]=g[x/2]+g[x/21]+1
ii)若 x为奇数: g [ x ] = 2 ∗ g [ x / 2 ] g[x]=2*g[x/2] g[x]=2g[x/2]

但这题 x 有点大
时间空间不允许记忆化搜索
我们设 g [ n ] = a ∗ g [ x ] + b ∗ g [ x − 1 ] + c g[n]=a*g[x]+b*g[x-1]+c g[n]=ag[x]+bg[x1]+c
我们只需要根据当前 x 的奇偶更新 x , a ,b ,c 即可直至 x==1 可发现是log级的

最终状态为 g [ n ] = a ∗ g [ 1 ] + b ∗ g [ 0 ] + c = c g[n]=a*g[1]+b*g[0]+c=c g[n]=ag[1]+bg[0]+c=c

故答案为 2^c % 2^64
可发现当 c>=64 时 2 ^ c | 2 ^ 64 故取模后为 0 可以省几次快速幂
不省的话会有点T 需要加个快读快写优化一下

Code

#include<bits/stdc++.h>
using namespace std;
#define __T int csT;scanf("%d",&csT);while(csT--)
const int mod=1e9+7;//2^64 ull自然溢出即可
const int maxn=1e5+3;

unsigned long long n,a,b,c;
unsigned long long qpow(unsigned long long dd,unsigned long long x)
{
    unsigned long long d=dd,sum=1;
    while(x>0)
    {
        if(x%2==1)sum=(sum*d);
        d=(d*d);
        x>>=1;
    }
    return sum;
}
inline void sol()
{
    scanf("%llu",&n);
    //g[n]=1*g[n]+0*g[n-1]+o*c;
    a=1;
    b=0;
    c=0;
    while(n>1)
    {
        if(n%2==0)
        {
            b=a+2*b;
            c=a+c;
            n/=2;
        }
        else
        {
            a=2*a+b;
            c=b+c;
            n=(n-1)/2;
        }
    }
    if(c>=64)puts("0");
    else printf("%llu\n", qpow(2,c));
}

int main()
{
    __T
        sol();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柯西可乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值