Codeforces 894.D Ralph And His Tour in Binary Country_ vector

给你一个树,边的连接方式是i与i/2连一条无向边,长度为l[i]。q个询问,每个询问给出一个点a和一个值H,求以a为起点到任意点为终点获得(happiness=H-路径长度)不为负数的所有happiness之和。

看了题解才弄懂的,每一个点递增有序地储存以它为根节点,所有子节点(包括自己)到它的距离并处理出前缀和。所需总空间为O(nlog(n)),然后对于每个询问,从询问点不断往上爬(编号/2就是了)并且减去边长,在每一层时,二分查询左右子树lch和rch(不是刚爬上来的那个子树)中满足条件(总路径小于H)的最大编号,然后就是直接利用前缀和贡献答案就是了。时间复杂度为O(nlog(n) + m(log(n))2)——使用归并排序的情况下(预处理时从下往上处理就可以使用归并了)。
 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>

using namespace std;

typedef long long ll;
const int maxn=1000010;
vector<ll> e[maxn],sum[maxn];
ll n,m,len[maxn],a,h;
ll ans;

ll query(ll x,ll h)
{
    if(h<=0)
        return 0;
    ll p = upper_bound(e[x].begin(),e[x].end(),h)-e[x].begin();
    return p*h-sum[x][p-1];
}

void init()
{
    for(int i=n;i>=1;i--)
    {
        e[i].push_back(0);
        int lc=i*2, rc=i*2+1;
        if(lc<=n)
            for(int j=0;j<e[lc].size();j++)
                e[i].push_back(e[lc][j]+len[lc]);
        if(rc<=n)
            for(int j=0;j<e[rc].size();j++)
                e[i].push_back(e[rc][j]+len[rc]);

        sort(e[i].begin(),e[i].end());
        sum[i].resize(e[i].size());
        for(int j=1;j<e[i].size();j++)
            sum[i][j]=sum[i][j-1]+e[i][j];

    }
}

int main() {

    cin>>n>>m;
    for(int i=2;i<=n;i++)
        scanf("%lld",&len[i]);

    init();
    while(m--)
    {
        ans=0;
        scanf("%lld%lld",&a,&h);

        ll last=0;
        while(a && h>0)
        {
            ans+=h;
            ll lc=2*a,rc=2*a+1;
            if(lc!=last && lc<=n)
                ans+=query(lc,h-len[lc]);
            if(rc!=last && rc<=n)
                ans+=query(rc,h-len[rc]);

            last=a;
            h-=len[a];
            a/=2;
        }
        printf("%lld\n",ans);

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c_czl/article/details/87913905