关于二叉树某点到各点的距离问题

题目链接
题意:给你一颗满二叉树,给出每个的边权,第i条边连接第i+1和第(i+1)/2的点
询问:x点到其他点的距离d[i] , d[i]-h>=0 的值累加的和
题解:构造树前缀和,将其每个子节点到该节点的值预处理出来,利用前缀和数组二分查找
具体看代码注释
代码转自https://www.cnblogs.com/ogiso-setsuna/p/7911772.html

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
int n,m,L[1000005],a,h,tmp,lch,rch,last;
ll ans=0;
vector<int>d[1000005];//存储子节点到该节点的距离
vector<ll>pre[1000005];//前缀数组
ll query(int x,int h)//二分查找在第x节点中子节点小于等于h的的个数
{
    if(h<=0) return 0;
    int p = upper_bound(d[x].begin(),d[x].end(),h)-d[x].begin();
    return 1ll*p*h-1ll*pre[x][p-1];//计算前缀和的累加
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++) scanf("%d",&L[i]);
    for(int i=n;i>=1;--i)//构建二叉树
    {
        d[i].pb(0);//本身距离
        lch=i<<1;rch=i<<1 | 1;//左右子节点
        if(lch<=n)
        {
            for(int j=0;j<d[lch].size();j++) d[i].pb(d[lch][j]+L[lch]);
        }
        if(rch<=n)
        {
            for(int j=0;j<d[rch].size();j++)d[i].pb(d[rch][j]+L[rch]);
        }
        sort(d[i].begin(),d[i].end());
        pre[i].resize(d[i].size());///开辟大小
        for(int j=1;j<pre[i].size();j++)
        {
            pre[i][j]=pre[i][j-1]+d[i][j];///前缀和
        }
    }
    while(m--)
    {
        scanf("%d%d",&a,&h);
        ans=0;
        tmp=a;last=0;//last用与判断之前的下层是否访问过
        while(tmp)//从询问点,慢慢往上计算不属于该点的子节点的 点
        {
            if(h<0) break;
            ans+=h;//加上自己本身
            lch=tmp<<1;rch=tmp<<1|1;
            if(lch<=n&&lch!=last)
            {
                ans+=query(lch,h-L[lch]);
            }
            if(rch<=n&&rch!=last)
            {
                ans+=query(rch,h-L[rch]);
            }
            h-=L[tmp];last=tmp;tmp>>=1;//返回上一层
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2017-12-13 17:04  Linese  阅读(235)  评论(0)    收藏  举报