解题思路
l a s t x last_x lastx表示最近的x 出现的位置,一开始全部赋为0.
p
i
p_i
pi 表示以i 结尾完美序列 的起点
递推式:
p
i
=
m
a
x
(
p
i
−
1
,
l
a
s
t
a
[
i
]
+
1
)
p_i=max(p_{i-1},last_{a[i]+1})
pi=max(pi−1,lasta[i]+1)
s t i , 0 st_{i,0} sti,0 表示以i结尾最长完美序列 的长度,很显然 f i = i − p i + 1 ; f_i=i-p_i+1; fi=i−pi+1;
然后我们友p的递推式可知,p是一个不下降序列,那么对于一个区间 [ l , r ] , 该 区 间 的 p 值 会 出 现 : 左 边 的 一 部 分 p 值 不 在 [l,r],该区间的p值会出现:左边的一部分p值不在 [l,r],该区间的p值会出现:左边的一部分p值不在[l,r] 区 间 内 , 而 右 边 的 一 部 分 的 p 值 在 区间内,而右边的一部分的p值在 区间内,而右边的一部分的p值在 [ l , r ] [l,r] [l,r]区间内,这个分界点可以二分得到,假设该位置为 n o w now now,则区间 [ l , n o w ] [l,now] [l,now]区间的p值在l左边,答案为 n o w − l + 1 , [ n o w + 1 , r ] now-l+1,[now+1,r] now−l+1,[now+1,r]区间内答案为f的最大值。
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int INF=1000100;
int n,m,s,t,len,ans,a[200010],last[2000010],p[200010],st[200010][50],lg[200010];
int find(int x,int y)
{
int l=x,r=y,ans=l;
while(l<=r)
{
int mid=(l+r)>>1;
if(p[mid]<x)
{
l=mid+1;
ans=mid;
}
else r=mid-1;
}
return ans;
}
int work(int x,int y)
{
// if(x>y)return 0;
int k=lg[y-x+1];
return max(st[x][k],st[y-(1<<k)+1][k]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
p[1]=1,last[a[1]+INF]=1,st[1][0]=1-p[1]+1;
for(int i=2;i<=n;i++)
{
p[i]=max(p[i-1],last[a[i]+INF]+1);
st[i][0]=i-p[i]+1;
last[a[i]+INF]=i;
}
lg[0]=-1;
for(int i=1;i<=n;i++)
lg[i]=lg[i>>1]+1;
for(int j=1;j<=lg[n];j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
for(int i=1;i<=m;i++)
{
ans=0;
scanf("%d%d",&s,&t);
s++,t++;
len=find(s,t);ans=len-s+1;
if(len<t)ans=max(ans,work(len+1,t));
printf("%d\n",ans);
}
}