题解:P12646 [KOI 2024 Round 1] 升序
注意到当给定序列
对于询问区间
考虑指针的移动次数是什么级别的。发现如果后面的位置
现在考虑如何得到序列
讨论一下
这样可以做到
::::info[Code]
#include <bits/stdc++.h>
#define pii pair<int,int>
#define pb emplace_back
#define int long long
#define mk make_pair
#define se second
#define fi first
#ifdef int
#define inf ((int)1e18+10)
#else
#define inf ((int)1e9+10)
#endif
#define mid ((l+r)>>1)
#define rs now<<1|1
#define ls now<<1
using namespace std;
bool Mst;
const int Max=2.5e5+10;
const int mod=998244353;
const double eps=1e-9;
int val[Max],sum[Max],n,q,a[Max],to[Max];
int solve(int l,int r){
int ans=sum[r]-sum[l-1];
int v=val[l],las=l;
while(1){
v=val[las];
int pos=to[las];
if(pos>r)pos=r+1;
ans-=(pos-las)*v;
las=pos;if(las==r+1)break;
}
return ans;
}
void init(){
stack<int>sta;
for(int i=1;i<=n;++i){
while(!sta.empty()&&val[sta.top()]>val[i]){
to[sta.top()]=i;sta.pop();
}
sta.push(i);
}
for(int i=1;i<=n;++i)if(!to[i])to[i]=n+1;
}
vector<pii>V;
bool Med;
signed main(){
n=read(),q=read();
for(int i=1;i<=n;++i)a[i]=read();
V.pb(mk(1,0));int res=1;
for(int i=1;i<=35;++i){res*=2;V.pb(mk(res,i));}
for(int i=2;i<=n;++i){
if(a[i]>a[i-1]){
int res=1.0*a[i]/a[i-1];
auto it=upper_bound(V.begin(),V.end(),mk(res,inf))-V.begin()-1;
int num=V[it].se;
val[i]=max(0ll,val[i-1]-num);
}
else{
int res=ceil(1.0*a[i-1]/a[i]);
auto it=lower_bound(V.begin(),V.end(),mk(res,0ll))-V.begin();
int num=V[it].se;
val[i]=val[i-1]+num;
}
sum[i]=sum[i-1]+val[i];
}
init();
for(int i=1;i<=q;++i){
int l,r;
l=read();r=read();
if(l==r){cout << "0\n";continue;}
cout << solve(l,r) << '\n';
}
cerr<< "Time: "<<clock()/1000.0 << "s\n";
cerr<< "Memory: " << abs(&Mst-&Med)/1000000.0 << "MB\n";
return 0;
}
/*
*/
::::info