HDU 6121 Build a tree(树 递归 17多校第七场)

  • 题目大意

    有一棵 n 个点的有根树,标号为0 n1 i 号点的父亲是i1k号点,求所有子树大小的异或和。
    1n,k1018

  • 分析

    题目中 i1k 这个的意思就是说这是一颗完全k叉树
    这里写图片描述
    我们容易发现以根节点的儿子为根的子树中,最多只有一颗子树是不满的,其他的都是满树。

    对于满树的情况我们可以很容易地求得答案。
    这样就只需要递归地取处理不满地那颗字数即可。
    注意k等于1的情况需要特别处理.

  • 代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<map>
#include<algorithm>
#include<set>
#include<stack>
using namespace std;
const int  MAXN=10000009;
#define LL long long int
int T;
LL n,k;
LL Get_h(LL n,LL k)//n个节点的k叉树的层数
{
    LL h=1;
    n=n-1;
    while(n!=0)
    {
        n=(n-1)/k;
        h++;
    }
    return h;
}
LL f(LL n,LL k)//当满树的情况的答案
{
    if(k%2==0)return n;
    else
    {
        LL h=Get_h(n,k);
        LL sum=0,x=0;
        for(int i=0;i<=h;i++)
        {
            x=x*k+1;
            sum=sum^x;
        }
        return sum;
    }
}
LL Get_n(LL h,LL k)//高度为h的满树的节点个数
{
    if(h==1)return 1;
    if(h==0)return 0;
    LL ans=0,x=1;
    for(int i=1;i<=h;i++)
    {
        ans+=x;
        x=x*k;
    }
    return ans;
}
LL Get_fa(LL n,LL k)//得到编号为n的那个节点离根节点最近的祖先的编号
{
    while((n-1)/k!=0){n=(n-1)/k;}
    return n;
}
LL F(LL n,LL k)//答案
{
    if(n==1)return 1;
    LL ans;
    LL h=Get_h(n,k);
    if(Get_n(h,k)==n)return f(n,k);
    LL n_l=Get_n(h-1,k);
    LL n_r=Get_n(h-2,k);
    LL fa=Get_fa(n-1,k);
    ans=F(n-1-(fa-1)*n_l-(k-fa)*n_r   ,   k);
    if(  (fa-1)%2==1)ans=ans^f(n_l,k);
    if(  (k-fa)%2==1)ans=ans^f(n_r,k);
    ans=ans^n;
    return ans;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld",&n,&k);
        if(k==1)
        {
             if(n%4==0)cout<<n<<endl;
             else if(n%4==1)cout<<1<<endl;
             else if(n%4==3)cout<<0<<endl;
             else cout<<n+1<<endl;
        }
        else cout<<F(n,k)<<endl;
    }
}
/*
100
1000000000000 100000
100000000000000000 1000000000
5 2
5 3
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值