URAL 1117

题目大意:一颗深度不超过31的满二叉树,按照中根遍历编号,求从a,a+1,a+2,...依次走到b时跳过的层的总数(a为m,n中的较小的数,b为较大的数)。

例如:

input:           output:

1 5              2(1->2,2->3,3->4(跳过一层),4->5(跳过一层))

Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

数据规模:1<=m,n<=2^31-1。

理论基础:满二叉树中根遍历时的编号规律,请自行百度。

题目分析:满二叉树中根遍历编号时,每个编号表示成二进制数时,不为0的最低位的位置(lowbit)为此节点所在的高度。例如:2,6的高度为2;4的高度为3;8的高度为4;1,3,5,7,...的高度为1。这个在求解处理时会用到。

这样的话我们可以得出:dp[i]=(dp[i-1]+i-2)<<1,dp[i]表示根节点为i的子树从左下角到右下角跳过的层的总数。

将所求分成两棵子树,求解即可。

代码如下:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<ctime>
#include<vector>
using namespace std;
typedef double db;
#define DBG 0
#define maa (1<<31)
#define mii ((1<<31)-1)
#define ast(b) if(DBG && !(b)) { printf("%d!!|\n", __LINE__); while(1) getchar(); }  //调试
#define dout DBG && cout << __LINE__ << ">>| "
#define pr(x) #x"=" << (x) << " | "
#define mk(x) DBG && cout << __LINE__ << "**| "#x << endl
#define pra(arr, a, b)  if(DBG) {\
    dout<<#arr"[] |" <<endl; \
    for(int i=a,i_b=b;i<=i_b;i++) cout<<"["<<i<<"]="<<arr[i]<<" |"<<((i-(a)+1)%8?" ":"\n"); \
    if((b-a+1)%8) puts("");\
}
template<class T> inline bool updateMin(T& a, T b) { return a>b? a=b, true: false; }
template<class T> inline bool updateMax(T& a, T b) { return a<b? a=b, true: false; }
typedef long long LL;
typedef long unsigned int LU;
typedef long long unsigned int LLU;
#define N 35
int a,b,dp[N];
int solve(int high)
{
    if(high==1)return 0;
    int r,nr=1;
    r=1<<30;
    while(!(r&high))r=r>>1;
    while(!(r&(1<<nr)))nr++;
    if(r==high)return dp[nr]+nr-1;
    return dp[nr]+((nr-1)<<1)+solve(high^r);
}
int main()
{
    while(~scanf("%d%d",&a,&b))
    {
        int r,nr=1;
        memset(dp,0,sizeof dp);
        if(a>b)a=a+b,b=a-b,a=a-b;
        r=1<<30;
        while(!(r&b))r>>=1;
        while(!(r&(1<<nr)))nr++;
        nr++;
        for(int i=2;i<=nr;i++)
        {
            dp[i]=(dp[i-1]+i-2)<<1;
        }
        dout<<nr<<endl;
        pra(dp,1,nr)
        printf("%d\n",solve(b)-solve(a));
    }
    return 0;
}

           其中,r代表右子树,l表示左子树,根据编号规律,找到比较大数小的最大的2^k的数,即可将原问题拆分成子树问题。

by:Jsun_moon http://blog.csdn.net/Jsun_moon

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值