完全二叉树+暴力预处理+归并排序——cf894D

/*
结点i的左儿子是2i,右儿子是2i+1 
那么显然这是一棵完全二叉树。
由于没有很好直接查询的办法,所以先考虑预处理一下这棵树
根据完全二叉树的性质,sum{size[i]}<=nlogn,所以直接用vector存下结点的所有孩子,自底向上对到子树距离归并排序

这样预处理完后,对于每个询问(A,H),我们只要以A为起点不断向上爬(最多logn次),通过lower_bound来计算贡献 
*/
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define N 1000005
#define ll long long

struct Edge{
    ll to,nxt,w;
}e[N<<1];
int head[N],tot;
void init(){
    memset(head,-1,sizeof head);
    tot=0;
}
void add(int u,int v,ll w){
    e[tot].to=v;e[tot].w=w;e[tot].nxt=head[u];head[u]=tot++;
}

vector<ll>dis[N];
int n,m;

vector<ll> merge(vector<ll> & fa, vector<ll> & son,ll w){
    vector<ll>res;res.clear();
    int p1=0,p2=0;
    while(1){
        if(fa[p1]<=son[p2]+w){
            res.push_back(fa[p1]);
            ++p1;
        }
        else {
            res.push_back(son[p2]+w);
            ++p2;
        }
        if(p1==fa.size() || p2==son.size())break;
    }
    while(p1<fa.size())
        res.push_back(fa[p1]),p1++;
    while(p2<son.size())
        res.push_back(son[p2]+w),p2++;
    return res;
}
void dfs(int u,int pre){
    int son=0;
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        son++;dfs(v,u);
    }
    dis[u].push_back(0);
    if(son==0)return;
    
    for(int i=head[u];i!=-1;i=e[i].nxt){
        int v=e[i].to;
        if(v==pre)continue;
        dis[u]=merge(dis[u],dis[v],e[i].w);
    }
}
vector<ll>sum[N];
void sumup(){
    for(int i=1;i<=n;i++)sum[i]=dis[i];
    for(int i=1;i<=n;i++){
        for(int j=1;j<sum[i].size();j++)
            sum[i][j]+=sum[i][j-1];
    }
}
ll W[N];

ll query(int u,ll H){//在子树u下查询 
    if(H<=0)return 0;
    ll res=0;
    int pos=lower_bound(dis[u].begin(),dis[u].end(),H)-dis[u].begin();
    if(pos==0)return 0;
    pos--;
    res=H*(pos+1)-sum[u][pos];
    return res; 
}

int main(){
    cin>>n>>m;
    init();
    for(int i=1;i<n;i++){
        ll w;scanf("%lld",&w);
        add((i+1)/2,i+1,w);
        add(i+1,(i+1)/2,w);
        W[i+1]=w;
    }
    dfs(1,1);
    sumup();
    
    while(m--){
        ll A,H;
        scanf("%lld%lld",&A,&H);
        ll sum=query(A,H);
        while(1){
            H-=W[A];
            if(A==1 || H<=0)break;
            int tmp=A;
            A/=2;//A=fa[A]
            sum+=H;
            for(int i=head[A];i!=-1;i=e[i].nxt){
                int v=e[i].to;
                if(v==tmp||v<A)continue;
                sum+=query(v,H-e[i].w);
            }            
        }
        cout<<sum<<'
';
    }
} 
原文地址:https://www.cnblogs.com/zsben991126/p/12299343.html