//对于结点i,本题如果能找到i的最左孩子和最右孩子的下标,便可以迎刃而解
//对于第i个结点,其前面有i-1个结点,每个结点各有m个孩子,再加上1号结点
//可得第i个结点的最左孩子下标为(i-1)*m+2
//同理可得第i个结点的最右孩子下标为i*m+1(前i个结点各有m个孩子,再加上1号结点)
//故对于子树根结点,只需逐层累加最右孩子-最左孩子即可
//只需要处理最后一层的特殊情况:
//1、最左孩子下标超出n,说明子树在最后一层没有结点,直接退出
//2、最右孩子下标超出n,说明子树在最后一层的结点是非满的,将最右孩子下标改为n,累加后退出
//有1e5组询问,对于每组询问,可在近似logm n的时间复杂度内得出结果,不会超时
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;//注意本题必须开long long,否则会挂掉绝大部分测试点
void solve()
{
int n,m,k;
ll cnt=1;//cnt记录以k为根的子树的结点总数,初始化为1(一个根结点)
//n为总结点数,m为树的叉数,k为待查询结点编号
scanf("%d%d%d",&n,&m,&k);
ll lchild=k,rchild=k;//开始计算最左孩子和最右孩子
while(1)
{
bool flag=0;
lchild=(lchild-1)*m+2;
rchild=rchild*m+1;
//cout<<"lchild = "<<lchild<<", rchild = "<<rchild<<endl;
if(lchild>n)break;//最左孩子超出n,本层为空,直接退出
if(rchild>=n)//最右孩子超出n
{
flag=1;//最后一次累加
rchild=n;//改为n
}
cnt+=rchild-lchild+1;//累加最左孩子与最右孩子之差
if(flag)break;
}
printf("%lld\n",cnt);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
solve();
}
return 0;
}
子树的大小 思维
最新推荐文章于 2024-07-25 11:27:31 发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)