6121 Build a tree(分治+思維)
阿新 • • 發佈:2019-01-22
題目大意:
給你一顆 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)));
}
}