标签:ret 大于 前缀和 前缀 ini end 完全 ORC i++
已知完全二叉树和每条边的权值,q次询问,每次给出sta起点和H。
w=(H-点到sta的权值),求w>0的所有w的加和。
这题用树上前缀和来写,e[i]记录子树上的点到点i的距离,sum[i][j]为e[i]的前缀和
这样每次找到满足大于h-len[i]的长度就行(二分查找)
void init(){ for(ll x=n;x>=1;x--){ e[x].push_back(0); ll lc=x<<1;ll rc=x<<1|1; if(lc<=n){ for(int i=0;i<e[lc].size();i++){ e[x].push_back(e[lc][i]+len[lc-1]); } } if(rc<=n){ for(int i=0;i<e[rc].size();i++){ e[x].push_back(e[rc][i]+len[rc-1]); } } sort(e[x].begin(),e[x].end()); sum[x].resize(e[x].size()); for(int i=1;i<e[x].size();i++){ sum[x][i]=sum[x][i-1]+e[x][i]; } } } ll query(int x,ll h){ if(h<=0)return 0; int index=upper_bound(e[x].begin(),e[x].end(),h)-e[x].begin(); return index*h-sum[x][index-1]; } int main() { // freopen("in.txt","r",stdin); int m; scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ scanf("%lld",&len[i]); } init(); while(m--){ ll a; ll h; ll pre=0; scanf("%lld%lld",&a,&h); ll ans=0; while(a&&h>0){ ans+=h; ll lc=a<<1; ll rc=a<<1|1; if(lc!=pre&&lc<=n){ ans+=query(lc, h-len[lc-1]); } // cout<<ans<<"\n"; if(rc!=pre&&rc<=n){ ans+=query(rc, h-len[rc-1]); } //cout<<ans<<"\n"; h-=len[a-1]; pre=a; a/=2; } printf("%lld\n",ans); } }
标签:ret 大于 前缀和 前缀 ini end 完全 ORC i++
原文地址:https://www.cnblogs.com/amitherblogs/p/12303978.html