题意:
给你一个有n个结点的完全K叉树,求每个子树的节点数量,并将他们异或输出。
思路:
从倒数第二层开始,找出其中的非满k叉树(每一层最多只有一个),其左边是now层的满K叉树,其右边是now-1层的满K叉树,再根据数量来分别决定是否异或其层数。然后在异或上当前非满K叉树。最后去到倒数i+1层,逐层判断即可,k>1时最多有59层,k=1时打表找规律··。
代码:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
ll num[60];
int init(ll n,ll k)
{
num[1] = 1;
int i;
for(i = 2;i<60;i++)
{
num[i] = num[i-1]*k+1;
if(num[i]>=n)
break;
}
return i;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll n,k;
scanf("%lld%lld",&n,&k);
if(k==1)
printf("%lld\n",n%4==0?n:(n%4==1?1:(n%4==2?n+1:0)));
else
{
int depth = init(n,k); //初始化k叉树前i层节点数,并得到总层数
//cout<<depth<<endl;
depth--;
int now = 2; //倒数第几层
ll pos = (n-2)/k;//非满二叉树的位置
ll ans = n;
if((n-num[depth])&1)
ans ^= 1;
while(depth>1)
{
ll ml = num[depth-1];
ll mr = num[depth]-1;
ll temp1 = num[now];
ll temp2 = num[now-1];
if((pos-ml)&1)
ans^=temp1;
if((mr-pos)&1)
ans^=temp2;
ll cnt = pos;
while(cnt<=(n-2)/k) //直接判断cnt*k+1<=n-1会爆long long, 变除法时尽量凑出等号,不然会WA
cnt = cnt*k+1;
ans ^= num[now-1]+n-cnt;
now++;
depth--;
pos = (pos-1)/k;
}
printf("%lld\n",ans);
}
}
return 0;
}