题目大意:
给你一颗 n 个节点的完全 k 叉树,问你这棵树中所有子树结点个数的总异或值。
分析:
首先是一个比较常见的结论,对于任意两个数 x y:x=x^y^y;
所以对于一颗完全 k 叉树,假设它有 t 层,那么我可以将它分解成三份,一份是若干个 t-1 层的满 k 叉树,一份若干个 t-2 层的满 k 叉树,还有一颗 t-1 层的不满的完全 k 叉树。因为异或运算的特殊性质,我就根据满 k 叉树的个数的奇偶和层数 t 就可以以接近 t 的时间复杂度得到每一份的答案(如果初始化,就能在O(1)内得到了)。同时相当于减小了所求树的规模,还是以 k 分的速度减少,这就很快了。
最后要注意:k=1的时候要特判,就是输出几百个找下规律就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
long long int n,k;
long long int comf(long long int x)//对于一颗深度为 x 层的满树,返回它的异或值
{
if(x<=0)return 0;
if(x==1)return 1;
long long int ans=1;
if(k%2==0)
{
long long int temp=1;
for(long long int i=1;i<x;i++)
{
temp*=k;
ans+=temp;
}
}
else
{
long long int temp=1;
for(long long int i=1;i<=x;i++)
{
temp*=k;
temp++;
ans=ans^temp;
}
}
return ans;
}
long long int sum(long long int x)//返回x层的满树有多少结点
{
if(x<=0)return 0;
if(x==1)return 1;
long long int ans=1;
for(long long int i=1;i<x;i++)
{
ans*=k;
ans++;
}
return ans;
}
long long int f(long long int n)//对于一颗 k 叉树,返回他的异或值
{
if(n==1)//只有一层
{
return 1;
}
/*if(n==2) { return 1^2; }*/
long long int temp=n-1;
long long int ln,rn;
long long int x=1;//层数
while((temp-1)/k>0)
{
temp=(temp-1)/k;
x++;
}
x++;
if(n==sum(x))return comf(x);
ln=temp-1;
rn=k-temp;
long long int ans=n;
//cout<<temp<<endl;
if(ln%2==1)
{
ans=ans^comf(x-1);
}
if(rn%2==1)
{
ans=ans^comf(x-2);
}
n=n-ln*sum(x-1)-rn*sum(x-2)-1;
//cout<<x-2;
//cout<<n;
ans=ans^f(n);
return ans;
}
int test=0;
int main()
{
/*test long long int ans=0; for(int i=1;i<=10000;i++) { ans=ans^i; printf("%d %lld\n",i,ans); } */
scanf("%d",&test);
while(test--)
{
scanf("%lld%lld",&n,&k);
if(k!=1)printf("%lld\n",f(n));
else printf("%lld\n",n%4==1?1:(n%4==2?n+1:(n%4==3?0:n)));
}
}