Codeforces 894.D Ralph And His Tour in Binary Country (预处理、二分)

题目链接:894D
题目意思:
 给你一个树,边的连接方式是i与i/2连一条无向边,长度为l[i]。q个询问,每个询问给出一个点a和一个值H,求以a为起点到任意点为终点获得(happiness=H-路径长度)不为负数的所有happiness之和。
 可以发现这个题目中所给出的树是一棵拍立非常整齐的二叉树(emmmmmm。。。类似于线段树的那种树形结构)
某个节点k的左儿子是2*k,右儿子是2*k+1。现在要求某个结点到其他任意结点的距离小于所给的H的H-len的和。由于询问是1e5次所以查询只能是logn之类。 看了题解才弄懂的,每一个点递增有序地储存以它为根节点,所有子节点(包括自己)到它的距离并处理出前缀和。所需总空间为O(nlog(n)),然后对于每个询问,二分一下查找这个结点中符合条件的加上(前缀和已经处理出来了)。同时这里要注意,在询问点不断往上爬的过程中在深度较低的结点不能加上已经爬过了的结点下符合条件的数(因为已经在前面加过了。。)。由于我不想再开两个vector,(可能会爆),所以我在深度较低的结点处理时,我先加上所有符合条件的点的数(<val-point),再在上一个结点找所有能够符合条件的数减去(<val-point-len[pre])注意,这里要再减去len[pre]。

#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6+9;
vector<long long > vec[MAX_N];
vector<long long > sum[MAX_N];
long long len[MAX_N];
void init()
{
    for(int i=0;i<MAX_N;i++)
    {
        vec[i].clear();
        sum[i].clear();
        vec[i].push_back(0);
    }
}
int main()
{
    int N,M,T;
    while(cin>>N>>M)
    {
        init();
        for(int i=2;i<=N;i++)
        {
            scanf("%lld",&len[i]);
        }
        // 预处理前缀和
        for(int i=N;i>=1;i--)
        {
            int t = i;
            long long int s = len[t];
            while(t/2)
            {
                int temp = t %2;
                t /= 2;
                vec[t].push_back(s);
                s += len[t];
            }
            sort(vec[i].begin(),vec[i].end());
            long long temp = 0;
            for(int j=0;j<vec[i].size();j++)
            {
                temp += vec[i][j];
                sum[i].push_back(temp);
            }
        }
        for(int i=0;i<M;i++)
        {
            long long ans = 0;
            long long point = 0;
            long long pre = 0;
            long long x,val;
            scanf("%lld%lld",&x,&val);
            while(x)
            {
                long long int pos = upper_bound(vec[x].begin(),vec[x].end(),val-point)-vec[x].begin();
                pos --;
                if(pos>=0)
                {
                   ans += (val-point)*(pos+1) - sum[x][pos];
                }
                if(pre != 0)
                {
                    pos = upper_bound(vec[pre].begin(),vec[pre].end(),val-point-len[pre]) - vec[pre].begin(); // 注意len[pre]不等于point
                    pos--;
                    if(pos>=0)
                    {
                        ans -= (pos+1)*(val-point) - (sum[pre][pos] + (pos+1)*len[pre]); //这个公式比较难推
                    }
                }
                point += len[x];
                if(val-point < 0) break;
                pre = x;
                x /= 2;
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值