Codeforces Round447 D树上前缀和

已知完全二叉树和每条边的权值,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);
    }
}

猜你喜欢

转载自www.cnblogs.com/amitherblogs/p/12303978.html
今日推荐