1. 程式人生 > >6121 Build a tree(分治+思維)

6121 Build a tree(分治+思維)

題目大意:

給你一顆 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))); } }