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:
22
3
输出样例1:
21
题意
求构造一棵使每个节点的左右子树的 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/2−1]∗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/2−1]+1
ii)若 x为奇数:
g
[
x
]
=
2
∗
g
[
x
/
2
]
g[x]=2*g[x/2]
g[x]=2∗g[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]=a∗g[x]+b∗g[x−1]+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]=a∗g[1]+b∗g[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;
}