SP744 LPERMUT - Longest Permutation
找到一个最长的子段,满足其重排后是 (1,2,3,4...k) ,其中 (k) 是子段长度。
一道思维题。
首先我们考虑找到这样的段的性质:序列长度为 (k) 且互不相同。
然后我们可以推出一个结论:这个序列中一定只包含一个某一个数 。
于是我们原数列中的 1 就相当于是把原数列分成了很多的段,于是如果我们可以每次以 1 为起点,然后向两边中的某一边扫一遍的话,就能够 (O(n)) 解决这个问题。
但是我们势必是要枚举两边的,这样做就是 (O(n^2)) 的了,于是我们可以考虑枚举一边,然后 (O(1)) 算另一边最长对应可以到达的位置。
这个该怎么算呢,我们假设目前匹配到的左端点是 (pos) ,然后当前右端点是 (i) (枚举 (i)),然后枚举下一个 (i) 的时候,答案的 (pos) 单调不增(如果此时会发生 (pos++) 了就代表遇到了两个同样的了。)。
那么一个点对于另外一个点的增加/减少有单调性,很明显就是双指针扫一遍了。
于是最后我们的复杂度为 (O(n)) 。
代码:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
const int N=3e5+5;
int n,a[N],ans;
int posl[N],posr[N],pos[N],Max[N],Maxpos[N];
void work(int s,int L,int R){
if(ans==0)ans=1;
Max[s]=a[s];
for(int i=s-1;i>=L;i--) Max[i]=max(Max[i+1],a[i]);
for(int i=s+1;i<=R;i++) Max[i]=max(Max[i-1],a[i]);
Maxpos[s]=posl[s];
for(int i=s-1;i>=L;i--) Maxpos[i]=max(Maxpos[i+1],posl[i]);
for(int i=s+1;i<=R;i++) Maxpos[i]=max(Maxpos[i-1],posl[i]);
for(int i=s-1;i>=L;i--){
if(Maxpos[i]>=i) continue;
int len=Max[i],r=i+len-1;
if(r>R) continue;
if(Maxpos[r]>=i||Max[r]>=Max[i]) continue;
if(len>ans) ans=len;
}
for(int i=s+1;i<=R;i++){
if(Maxpos[i]>=s) continue;
int len=Max[i],l=i-len+1;
if(l<L) continue;
if(Maxpos[l]>=l||Maxpos[i]>=l||Max[l]>=Max[i]) continue;
if(len>ans) ans=len;
}
return ;
}
int main(){
read(n);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<=n;i++) posl[i]=pos[a[i]],pos[a[i]]=i;
for(int i=1;i<=n;i++) pos[i]=n+1;
for(int i=n;i>=1;i--) posr[i]=pos[a[i]],pos[a[i]]=i;
for(int i=1;i<=n;i++) if(a[i]==1) work(i,posl[i]+1,posr[i]-1);
write(ans);
return 0;
}