P7167 [eJOI 2020 Day1] Fountain

传送门

题意

在这里插入图片描述

按顺序从上到下给出n个石板,每一块容量 c i c_i ci,直径 d i d_i di q q q次询问,每一次询问:从 a a a个石板倒 b b b个水,问最后能停留在哪个石板。水只能往下流,且只能被比自己直径大的石板接到水。

解析

很容易联想到单调栈,单调栈可以过掉一般数据,但是如果遇到递增的序列,单调栈每次查询的时间复杂度就会很高。

因此考虑用ST表来处理数据,进行倍增处理。
n e [ i ] [ j ] ne[i][j] ne[i][j]代表从 i i i开始,往后数 2 j 2^j 2j个满足能把从 i i i倒下来的水接到的石块的下标。 s u m [ i ] [ j ] sum[i][j] sum[i][j]代表从 i i i开始,往后 2 j 2^j 2j之和,包括 i i i

大概思路是这样,先把某个数字右边比其大的几个用ne维护好,全部链接起来,遍历的时候,先从大区间开始遍历,然后慢慢缩小,当 k k k不满足,而 k / 2 k/2 k/2满足时候,减掉值,并且跳转到 k / 2 k/2 k/2这个位置,继续查看这个位置以后长度为 k / 4 k/4 k/4是否满足。
在这里插入图片描述
假设我们就3个水,处理完以后就会跳到2这个位置

时间复杂度
单调栈 O ( N ) O(N) O(N),倍增 O ( N l o g N ) O(NlogN) O(NlogN),回答询问 O ( Q l o g N ) O(QlogN) O(QlogN),总复杂度 O ( ( N + Q ) l o g N O((N+Q)logN O((N+Q)logN

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int d[N],c[N];
int ne[N][31],sum[N][31];
int st[N],t;
int n,q;

void init(){
    for(int i=1;i<=n;i++){
        sum[i][0]=c[i];
    }

    for(int i=1;i<=23;i++){
        for(int j=1;j<=n;j++){
            ne[j][i]=ne[ne[j][i-1]][i-1];
        }
    }

    for(int i=1;i<=23;i++){
        for(int j=1;j<=n;j++){
            sum[j][i]=sum[j][i-1]+sum[ne[j][i-1]][i-1];
        }
    }
}

int main(){
    cin>>n>>q;
    for(int i=1;i<=n;i++)cin>>d[i]>>c[i];
    for(int i=n;i>=1;i--){
        while(t && d[st[t]]<=d[i])t--;
        ne[i][0]=st[t];
        st[++t]=i;
    }
    init();
    while(q--){
        int a,b;
        cin>>a>>b;
        for(int i=23;i>=0;i--){
            if(sum[a][i]<b){
                b-=sum[a][i];
                a=ne[a][i];
            }
        }
        cout<<a<<endl;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值