[JOISC 2024 Day1] 鱼 3 题解
观察性质题。下称“投喂
考虑暴力怎么做。显然一个
考虑另一种“暴力”,就是将询问离线,按
上面的这种暴力看起来很能找性质进行优化。观察到如果我们更新过了一对相邻的数,那么接下来不管怎么更新,它们的差不变!证明:每次更新都是对一个
放代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
inline int ceil_div(int x,int y){
return x/y+!!(x%y);
}
class segtree{
private:
int n;
vector<pii> B;
vector<int> S,T;
public:
inline void pushup(int u){
S[u]=S[u<<1]+S[u<<1|1];
}
inline int size(int u){
return B[u].second-B[u].first+1;
}
inline void pushdown(int u){
S[u<<1]+=T[u]*size(u<<1),S[u<<1|1]+=T[u]*size(u<<1|1);
T[u<<1]+=T[u],T[u<<1|1]+=T[u],T[u]=0;
}
segtree(int N){
n=N,B.resize(n<<2),S.resize(n<<2),T.resize(N<<2);
function<void(int,int,int)> build=[&](int u,int l,int r){
if(B[u]=make_pair(l,r);l==r)return;
int m=l+r>>1;
build(u<<1,l,m),build(u<<1|1,m+1,r);
};
build(1,0,n-1);
}
inline void update(int u,int l,int r,int x){
if(B[u].first>r||B[u].second<l)return;
if(l<=B[u].first&&B[u].second<=r){
T[u]+=x,S[u]+=x*size(u); return;
}
pushdown(u);
update(u<<1,l,r,x),update(u<<1|1,l,r,x);
pushup(u);
}
inline int query(int u,int l,int r){
if(B[u].first>r||B[u].second<l)return 0;
if(l<=B[u].first&&B[u].second<=r)return S[u];
pushdown(u);
return query(u<<1,l,r)+query(u<<1|1,l,r);
}
}; // 线段树
main(){
ios::sync_with_stdio(false);
int n,d; cin>>n>>d;
vector<int> c(n);
for(auto &i:c)cin>>i;
int q; cin>>q;
vector<vector<pii> > Q(n);
vector<int> R(q);
for(int i=0;i<q;i++){
int l,r; cin>>l>>r;
Q[r-1].emplace_back(l-1,i);
} // 把询问离线下来
segtree t(n);
auto get=[&](int p){return c[p]-t.query(1,p,p)*d;};
stack<int> s;
for(int i=0;i<n;i++){
int r=i;
while(!s.empty()){
int l=s.top(),x=get(r-1),y=get(r);
if(x<=y)break;
t.update(1,l,r-1,ceil_div(x-y,d));
r=l,s.pop();
} // 弹栈顶,均摊 N 次
s.emplace(r);
for(auto [l,w]:Q[i])
R[w]=(get(l)<0?-1:t.query(1,l,i));
}
for(int i:R)cout<<i<<'\n';
return 0;
}