hdu--6121--Build a tree

来源:互联网 发布:js bind 原理 编辑:程序博客网 时间:2024/06/05 02:08

Build a tree

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 535    Accepted Submission(s): 167


Problem Description
HazelFan wants to build a rooted tree. The tree has n nodes labeled 0 to n1, and the father of the node labeled i is the node labeled i1k. HazelFan wonders the size of every subtree, and you just need to tell him the XOR value of these answers.
 

Input
The first line contains a positive integer T(1T5), denoting the number of test cases.
For each test case:
A single line contains two positive integers n,k(1n,k1018).
 

Output
For each test case:
A single line contains a nonnegative integer, denoting the answer.
 

Sample Input
25 25 3
 

Sample Output
76
 
题目大意:

题目给出节点数为n的满k叉树,求出这棵满k叉树所有子树节点个数的异或总和。

解题思路:

对于所有的子树,可能会存在一棵不是满k叉树,就是编号为n-1的节点所在的那棵树,就把这棵子树的根节点作为

分界点,由满k叉树的性质知道,这个分界点的左边为满k叉树,而这个点的右边是比左边少一层的满k叉树。由此

可以得出一个结论,在每一层有三种情况,满二叉树,不满的二叉树,以及少一层的满k叉树。从叶子节点开始向上递归处理,把这三种情况分开处理。满的k叉树的节点个数比较容易得出,对于那个不满的,可以用右边的那种情况,加上多出来的节点数。需要注意的是k为1的情况要进行特殊处理。

更多细节性的东西,代码中有注释:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <bits/stdc++.h>
#define ll long long
#define maxn 105
using namespace std;

ll n, k;
ll num[maxn];

ll power(ll a, ll n)
{
    ///快速幂
    ll ans = 1;
    while(n)
    {
        if(n & 1)
            ans *= a;
        a *= a;
        n >>= 1;
    }
    return ans;
}

void init(ll k, ll depth)
{
    ///预处理出完全k叉树前i层节点个数和
    for(int i = 1; i <= depth; i++)
        num[i] = (power(k, i) - 1) / (k - 1); ///k^0+k^1+k^2....k^i,(每一层为k^i);
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        ll ans;
        scanf("%lld%lld", &n, &k);
        if(k == 1)                ///变为为链表
        {
            ll temp = n % 4;
            if(temp == 0)
                ans = n;
            else if(temp == 1)
                ans = 1;
            else if(temp == 2)
                ans = n + 1;
            else
                ans = 0;
            printf("%I64d\n", ans);
            continue;
        }
        int depth = 1;            ///求出树的高度
        ll res = n - 1;
        while(res > 0)
        {
            res = (res - 1) / k;  ///求父节点,
            depth++;              ///高度加一
        }
        init(k, depth);
        ans = n;                  ///根节点为0的子树
        if((n - num[depth - 1]) & 1///异或可以相互抵消
            ans ^= 1;
        depth--;
        ll pos = (n - 1 - 1) / k; ///临界点在以编号为pos节点为根的子树上
        int now = 2;              ///当前是从下往上第几层
        while(depth > 1)         ///depth等于1,即变为以0为根节点的子树时,结束递归
        {
            ll left = num[depth - 1]; ///当前层最左边节点的编号
            ll right = num[depth] - 1///当前层最右边节点的编号
            ll temp1 = num[now];   ///当前层满k叉树  子树节点个数
            ll temp2 = num[now - 1]; ///当前层满k叉树  层数减1后     子树节点个数
            if((pos - left) & 1)   ///临界点左边满k叉树的个数为奇数,那么结果异或上节点个数,否则异或相互抵消,不计
                ans ^= temp1;
            if((right - pos) & 1)  ///同理
                ans ^= temp2;
            ll cnt = pos;
            while(cnt <= (n - 1 - 1) / k) ///由于cnt*k+1<=n-1,左边会爆long long,故转化为除法。
                cnt = cnt * k + 1///求出这个不满k叉树最左边的子叶编号,则n-cnt就是这个不满k叉树的子叶个数,拿右边少一层的满k叉树节点个数加上n-cnt就是这个不满k叉树的节点数
            ans ^= num[now - 1] + n - cnt; ///临界点的子树特殊处理
            now++;
            depth--;
            pos = (pos - 1) / k; ///界限向上推一个
        }
        printf("%I64d\n", ans);
    }
    return 0;
}