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 n−1, and the father of the node labeled i is the node labeled ⌊i−1k⌋. 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(1≤T≤5), denoting the number of test cases.
For each test case:
A single line contains two positive integers n,k(1≤n,k≤1018).
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
2017 Multi-University Training Contest - Team 7
/* * @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; }