专栏文章
题解:P12646 [KOI 2024 Round 1] 升序
P12646题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min9zp89
- 此快照首次捕获于
- 2025/12/01 22:59 3 个月前
- 此快照最后确认于
- 2025/12/01 22:59 3 个月前
注意到当给定序列 之后我们可以根据每个点的操作次数得到一个序列 。这个 就是每个位置的修改次数的上限,那么考虑当已知 的情况下如何快速求解每个询问。
对于询问区间 ,可以知道询问时的 一定是 ,可以知道,当一个点的 减少 ,那么他后面的一段非零连续段都可以减少一次操作。现在是第 个位置少操作了 次,那么其后面的一段大于等于 的连续段都可以减少 。这样我们跳到 后面第一个位置 满足 ,这个时候由于有上述性质,那么最后询问的 也是 ,那么变成子问题 的答案。那么当我们使用单调栈维护右侧第一个小于当前点的位置即可做到 移动指针。
考虑指针的移动次数是什么级别的。发现如果后面的位置 比前面位置 的 值少 ,那么在原序列 中 至少是 的两倍,这样才能满足条件。这样可以知道指针的移动次数是 级别的。
现在考虑如何得到序列 。这是不能直接暴力模拟的,不如 会很大。我们可以用 来计算 。于是有如下式子:
讨论一下 和 的大小关系,若 ,可以得到 。这是很好计算的。另一种情况同理。
这样可以做到 的复杂度。
Code
CPP#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
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...