题目链接
题意:给你一颗满二叉树,给出每个的边权,第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
",ans);
}
return 0;
}