由于是完全二叉树,所以我们可以预先知道整棵树的形状,因此可以判断根节点的两个子节点哪个是满二叉树,哪个不是满二叉树(必然是一边满,一边不满),对于满的子节点,我们可以直接求出它的不同子树的个数,也就是说我们只要递归搜不满的子节点就行了,这样一来,我们的复杂度就只有logn了。
当然还要解决相同子树判重的问题(只有满二叉子树才会出现重复),这里我用了vis数组来标记已经计算过的子树(例如vis[i],代表树高为i+1的满二叉树,这里注意标记了树高为i的满二叉树,那么所有树高比i+1小的也都要标记掉)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 6 typedef long long LL; 7 8 const int maxn = 111; 9 10 LL _n; 11 12 LL sum[maxn], f[maxn]; 13 int vis[maxn]; 14 LL solve(LL n) { 15 int po = 0; 16 for (int i = 62; i >= 0; i--) { 17 if (sum[i] == n) { 18 LL tmp = 0; 19 for (int j = i; j >= 0 && vis[j] == 0; j--) { 20 tmp++; 21 vis[j] = 1; 22 } 23 return tmp; 24 } 25 else if (sum[i] < n) { 26 po = i; break; 27 } 28 } 29 //printf("po:%d ", po); 30 LL ret = 1; 31 LL res = n - sum[po]; 32 if (res > f[po]) {//搜右边 33 for (int i = po; i >= 0 && vis[i] == 0; i--) { 34 ret++; 35 vis[i] = 1; 36 } 37 ret+=solve(n - sum[po]-1); 38 } 39 else {//搜左边 40 for (int i = po-1; i >= 0 && vis[i] == 0; i--) { 41 ret++; 42 vis[i] = 1; 43 } 44 if(po>=1) ret += solve(n - sum[po - 1] - 1); 45 else ret += solve(n - 1); 46 } 47 return ret; 48 } 49 50 void get_table() { 51 f[0] = 1; 52 for (int i = 1; i < 63; i++) f[i] = f[i - 1] << 1; 53 sum[0] = 1; 54 for (int i = 1; i < 63; i++) sum[i] = sum[i - 1] + f[i]; 55 } 56 57 void init() { 58 memset(vis, 0, sizeof(vis)); 59 } 60 61 int main() { 62 63 get_table(); 64 /* 65 for (int i = 0; i < 10; i++) printf("%lld ", f[i]); 66 printf(" "); 67 for (int i = 0; i < 10; i++) printf("%lld ", sum[i]); 68 printf(" "); 69 */ 70 while (scanf("%lld", &_n) == 1) { 71 init(); 72 LL ans = solve(_n); 73 printf("%lld ", ans); 74 } 75 return 0; 76 }