Auto X2021 K Increasing Sequence

还是姿势水平不太行,这道题其实很简单。

很容易发现最后的序列一定是一个上升序列,并且值域是(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;
}

原文地址:https://www.cnblogs.com/ZH-comld/p/15416260.html