还是姿势水平不太行,这道题其实很简单。
很容易发现最后的序列一定是一个上升序列,并且值域是(1-32)的,很小。
进一步的,我们可以发现,删数可以随意删,只要我们能尽量合成较大的数就行了。
我们可以令(f[j][i])表示从(j)开始,要合成一个(i),最近会跳到什么位置。
这个数组显然是用来倍增的,我们枚举的时候可以从左向右枚举一下,维护出这个数组。
对于询问,直接从右端点向前跳即可。
#include<bits/stdc++.h>
#define N 100009
using namespace std;
typedef long long ll;
int f[N][41];
int n,m,a[N];
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
int main(){
int T=rd();
while(T--){
n=rd();m=rd();
for(int i=0;i<=n;++i){
for(int j=0;j<=40;++j)f[i][j]=-1;
}
for(int i=1;i<=n;++i){
a[i]=rd();
}
for(int i=1;i<=40;++i){
int now=-1;
for(int j=1;j<=n;++j){
if(a[j]<i){
if(f[j][i-1]!=-1&&f[f[j][i-1]][i-1]!=-1)f[j][i]=f[f[j][i-1]][i-1];
else f[j][i]=now;
}
else if(a[j]==i)f[j][i]=j-1,now=j-1;
else now=-1;
}
}
while(m--){
int l=rd();int r=rd();
int ans=0;
for(int i=40;i>=1;--i)if(f[r][i]>=l-1){
ans+=i;
r=f[r][i];
}
printf("%d
",ans);
}
}
return 0;
}