题目大意
给定长度为N的序列,权值范围在[1,n]。
求一个长度最大的区间使该区间是一个包含1的排列。
n<=1e6,空间限制16M
做法
一个区间合法的条件?
1、包含1
2、所有元素互不相同
3、若长度为k,区间和为k*(k+1)/2
这样的区间的性质?
最大值等于区间长度。
这样的区间最大值可以在1左边或右边,做两次即可,下面假设最大值在1右边。
枚举每一个1,然后枚举右端点的位置(显然在该1右边)
假如枚举的右端点位置是r,1的位置是i,[i,r]最大值为mx。根据性质,现在枚举的区间就是[r-mx+1,r]。
为了满足条件2,我们需要预处理left表示一个数左边第一个和其相同的位置(这个预处理需要两个数组),那么我们求出[i,r]的left的max,便可以判断[r-mx+1,r]是否合法,同时为了满足条件3,需要用前缀和来计算。
复杂度显然线性。
前缀和数组应该是个long long算两个数组,加上原数组,计算left所用两个数组,总共是5个数组,boom。
原数组和前缀和数组显然可以共用,总共是4个数组,boom。
left不要预处理,即算即用,3个数组,可以。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1000000+2;
typedef long long ll;
ll a[maxn];
int last[maxn];
int i,n,ans,mx,wz;
void calc(){
int j,l;
ans=max(ans,1);
mx=1;
l=last[a[i]-a[i-1]];
last[a[i]-a[i-1]]=i;
wz=l+1;
fo(j,i+1,n+1){
if (j>n||a[j]-a[j-1]==1) break;
l=last[a[j]-a[j-1]];
last[a[j]-a[j-1]]=j;
wz=max(wz,l);
mx=max(mx,int(a[j]-a[j-1]));
if (wz<=j-mx+1)
if (a[j]-a[j-mx]==(ll)mx*(mx+1)/2) ans=max(ans,mx);
}
i=j;
}
int main(){
scanf("%d",&n);
fo(i,1,n){
scanf("%d",&a[i]);
//sum[i]=sum[i-1]+(ll)a[i];
//left[i]=last[a[i]];
//last[a[i]]=i;
}
fo(i,1,n) a[i]+=a[i-1];
fo(i,1,n){
if (a[i]-a[i-1]==1) break;
last[a[i]-a[i-1]]=i;
}
while (i<=n){
calc();
}
fd(i,n,1) a[i]-=a[i-1];
fo(i,1,n/2) swap(a[i],a[n-i+1]);
fo(i,1,n) a[i]+=a[i-1];
fo(i,1,n) last[i]=0;
fo(i,1,n){
if (a[i]-a[i-1]==1) break;
last[a[i]-a[i-1]]=i;
}
while (i<=n){
calc();
}
printf("%d\n",ans);
}