HDU 6121 Build a tree(找规律+模拟)

Build a tree

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


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
2 5 2 5 3
 

 

Sample Output
7 6
 

 

Source
 

 

/*
* @Author: lyuc
* @Date:   2017-08-15 14:04:25
* @Last Modified by:   lyuc
* @Last Modified time: 2017-08-16 22:11:31
*/

/*
 题意:给你一个n个节点的k叉树,然后让你求每个子树节点个数的异或和

 思路:
     当k等于1的时候处理会超时的,但是有规律:
         n%4=0    结果为n
         n%4=1    结果为1
         n%4=2    结果为n+1
         n%4=3    结果为0
     当n<=k+1时:
         如果      n%2==1
        结果      n
        

        如果      n%2==0
        结果      n+1
    如果是个完全k叉树:
        如果     k%2==0
        结果     n 

        如果     k%2==1
        结果     每层异或一个
    剩余的情况中:
        root节点的子树中最多只有一个子树是不完全k叉树,这棵树单独处理,然后剩下的是满k叉树
    按照上面的办法求

*/

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define MAXN 105

using namespace std;

int t;
LL n,k;
LL res;
LL num[MAXN];

inline void init(){
    res=0;
}

int main(){ 
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d",&t);
    while(t--){
        init();
        scanf("%lld%lld",&n,&k);
        if(n<=k+1){//如果是矩阵上三角
            if(n%2==1){
                printf("%lld\n",n);
            }else{
                printf("%lld\n",n+1);
            }
        }else{
            if(k==1){//k=1的时候需要特殊处理
                switch(n%4){
                    case 0:
                        printf("%lld\n",n);
                        break;
                    case 1:
                        puts("1");
                        break;
                    case 2:
                        printf("%lld\n",n+1);
                        break;
                    case 3:
                        puts("0");
                        break;
                }
            }else{
                //判断是不是完全树
                LL deepth=1;//树的深度(不包括最后一行,根节点深度为0)
                LL cur=1;
                bool flag=false;
                num[1]=1;
                while(num[deepth]<n){
                    deepth++;
                    cur*=k;
                    num[deepth]=num[deepth-1]+cur;//计算出根节点到每层的节点数
                    if(num[deepth]==n){
                        flag=true;
                        break;
                    }
                }
                if(flag==true){//如果是完全树
                    if(k%2==0){//偶数叉树,异或到最后只是一个根节点了
                        printf("%lld\n",n);
                    }else{//奇数叉树,异或到最后每层剩一个节点
                        cur=1;
                        while(cur<n){
                            res^=cur;
                            cur*=k;
                        }
                        printf("%lld\n",res);
                    }
                }else{//如果不是完全树
                    // 整棵树
                    res = n;
                    
                    // 最底层单独做
                    res ^= (n - num[deepth-1]) & 1;

                    --deepth;
                    LL p = (n - 2) / k; // [(n - 1) - 1] / k,倒数第 2 层开始
                    LL lid, rid, lnum, rnum, lch;
                    for(LL d = 2; p > 0; p = (p - 1) / k, ++d, --deepth)
                    {
                        // 当前层最左边结点的标号
                        lid = num[deepth-1];

                        // 当前层最右边结点的标号
                        rid = num[deepth] - 1;
                        
                        // 左边的子树(满的)的大小
                        lnum = num[d];

                        // 右边的子树(少一层,但也是满的)的大小
                        rnum = num[d - 1];

                        if((p - lid) & 1)
                            res ^= lnum;

                        if((rid - p) & 1)
                            res ^= rnum;

                        lch = p; // p 为根的子数最左小角的后代结点

                        while(lch <=(n - 2) / k) // lch * k + 1 <= n - 1
                            lch = lch * k + 1;

                        res ^= num[d-1] + n - lch;
                    }
                    printf("%lld\n",res);
                }
            }
        }
    }
    return 0;
}

 

posted @ 2017-08-16 22:14  勿忘初心0924  阅读(192)  评论(0编辑  收藏  举报