YBTOJ C. 【例题3】与众不同

思路:首先应该想到dp,我们约定pre[i] 为i号位为结尾的数的起始位置,f[i]为i号位为结尾的最长序列长度,不难得到 pre[i]=max(pre[i-1],last[a[i]]+1)   f[i]=i-pre[i]+1

对于一个访问区间有两种序列长度:1.整个序列被包含在区间内 2.整个序列被不完全包含在区间内

因为上面的式子可以得到pre[i] 为一个单调不降序列,于是我们可以用二分来寻找分界点,那么第二种序列长度就为pos-l+1,下面就是处理第一种答案了,这里运用RMQ算法来加速区间最大值查询

注意:a[i]有负值,需要将所有元素全加上1e6来保证数去全为正以防爆数组

Code

#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
	int sum=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
	while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
	return sum*f;
}
const int N=2e5+10;
const int M=1e6+10;
int n,m,a[N],pre[N],f[N][30],lg[N],last[M+M+1000];
inl void init(){
	lg[0]=-1;
	for(re int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
}
inl int find(int l,int r){
	int now=l;
	int x=l,y=r;
	while(x<=y){
		int mid=x+y>>1;
		if(pre[mid]<l) now=mid,x=mid+1;
		else y=mid-1;
	}
	return now;
}
inl int query(int l,int r){
	int k=lg[r-l+1];
	return max(f[l][k],f[r-(1<<k)+1][k]);
}
signed main(){
	n=read(),m=read();
	init();
	for(re int i=1;i<=n;i++) a[i]=read();
	pre[1]=1;last[a[1]+M]=1;
	f[1][0]=1;
	for(re int i=2;i<=n;i++){
		pre[i]=max(pre[i-1],last[a[i]+M]+1);
		last[a[i]+M]=i;f[i][0]=i-pre[i]+1;
	}
	for(re int j=1;j<=lg[n];j++){
		for(re int i=1;i+(1<<j)-1<=n;i++){
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		}
	}
	for(re int i=1;i<=m;i++){
		int l=read(),r=read();
		l++,r++;
		int pos=find(l,r);
		int ans=pos-l+1;
		if(pos<r) ans=max(ans,query(pos+1,r));
		printf("%lld\n",ans);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值