RMQ问题:与众不同(st表的高端应用)

在这里插入图片描述

解析

预处理

用pre[i]表示以i结尾的最长完美序列起始点,用last[i]表示数字i最后出现的位置
那么可以得到递推式:

pre[i]=max(pre[i-1],last[x[i]]+1);

也就是说这个pre要么是受前一位一样的限制,要么是受自己的限制
用f[i]表示以i结尾的最长完美序列长度,那么显然:

f[i]=i-pre[i]+1;

以上可以用O(n)完成预处理

询问

接下来对于每次询问区间 [l,r] 中的每一位,只有两种可能:
1.pre在l左侧或l上
2.pre在l右侧
根据之前的递推式,pre显然单调不减
因此这两种情况的分布具有单调性,我们就可以二分找出一个结点mid,使左侧及本身的pre均<=l(称为A),右侧pre均>l(称为B)
A:对于A的每一个元素,其在区间内可以达到的长度应该为i-l+1;那么显然让i=A中最大的值——mid时,取到最大值
B:对与B中的每一个元素,以其结尾的最大长度就是f[i],因此B的最优就是f在[now+1,r]中的最大值,用st表维护即可
A和B各自的最优取max,就是答案
单次询问时间复杂度O(logn)

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <string>
#include<map>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
const int N=2e5+100;
const int M=2e6+100;
int n,m,mod,ask;
int a,b,c;
int x[N],pre[N],last[M],f[N];
int dp[N][30],q[N];
int maxx(int a,int b){
	int k=q[(b-a)+1];
	int ans=max(dp[a][k],dp[b-(1<<k)+1][k]);
	return ans;
}
void Dp(){
	int o=0;
	for(int i=1;i<=n;i++){
		if(1<<o<=i) o++;
		q[i]=o-1;
	}
	for(int i=1;i<=n;i++) dp[i][0]=f[i];
	for(int k=1;k<=q[n];k++){
		for(int i=1;i+(1<<k)-1<=n;i++) dp[i][k]=max(dp[i][k-1],dp[i+(1<<(k-1))][k-1]);
	}
}
int find(int l,int r){
	int st=l,ed=r;
	while(st<ed){
		int mid=(st+ed+1)>>1;
		if(pre[mid]<=l) st=mid;
		else ed=mid-1;
	}
	return st;
}
int query(int l,int r){
	int now=find(l,r);
	int ans=now-l+1;
//	int ans=0;
//	for(int i=l;i<=r;i++){
//		if(pre[i]<=l) ans=max(ans,i-l+1);
//		else ans=max(ans,f[i]);
//	}
	//ans=max(ans,maxx(now+1,r));
//	for(int i=now+1;i<=r;i++) ans=max(ans,f[i]);
	ans=max(ans,maxx(now+1,r));
	return ans;
}
void read(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&x[i]);
}
void solve(){
	for(int i=1;i<=n;i++){
		pre[i]=max(pre[i-1],last[x[i]]+1);
		last[x[i]]=i;
		f[i]=i-pre[i]+1;
//		printf("%d ",pre[i]);
	}
//	printf("\n");
	Dp();
}
void putout(){
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);
		a++;b++;
		if(pre[b]<=a){
			printf("%d\n",b-a+1);
			continue;
		}
		printf("%d\n",query(a,b));
	}
}
int main(){
	read();
	solve();
	putout();
    return 0;
}
/*
9 15
1 2 3 4 5 6 7 8 9
*/

疑问

仔细看代码的童鞋会发现我的数组都开了2e6,但除了last之外应该开到2e5(n的范围)就够了
但一开始开2e5wa掉了。。。
期待看破其中玄机的dl评论区指点迷津awa
谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值