题意
按顺序从上到下给出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;
}
}