[bzoj3100]排列

来源:互联网 发布:淘宝导航栏代码生成器 编辑:程序博客网 时间:2024/06/10 04:20

题目大意

给定长度为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);}
0 0
原创粉丝点击