Time Limit: 6000MS | Memory Limit: 65536K | |
Total Submissions: 10299 | Accepted: 2729 |
Description
Input
Output
Sample Input
4 5 4 3 6 4 6 5 4 3
Sample Output
1-1
题意:在给定序列中求出一个区间,最小值是左边界,最大值是右边界。求最大区间长度。 第一个样例是(3,6),第二个样例因为是递减的所以不存在满足条件的区间。输出-1
思路:用RMQ存下最大值与最小值,然后枚举区间左边界,再用二分法找出满足“最小值是左边界”这个条件的最大区间的右边界编号(也就是说这个右边界编号右边要么比左边界小,要么右边界已经是序列最后一个数了),然后再这个区间里求出最大值,就是要求的区间了。 我们可以假设一下,如果最大区间右边界不是前面求出来的区间的最大值,那么必然结果的右边界要在最大值编号右边,那么就不满足最大值是右边界这个条件了。 最后枚举完求出最大值就OK。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define N 50050 #define maxn 100050 int a[N]; int index[maxn]; int dpmax[N][20]; int dpmin[N][20]; void makemax(int n) { for(int i=0; i<n; i++) dpmax[i][0]=a[i]; for(int j=1; (1<<j)<=n; j++) { for(int i=0; i+(1<<j)-1<=n; i++) { dpmax[i][j]=max(dpmax[i][j-1],dpmax[i+(1<<(j-1))][j-1]); } } } void makemin(int n) { for(int i=0; i<n; i++) dpmin[i][0]=a[i]; for(int j=1; (1<<j)<=n; j++) { for(int i=0; i+(1<<j)-1<=n; i++) { dpmin[i][j]=min(dpmin[i][j-1],dpmin[i+(1<<(j-1))][j-1]); } } } int getmax(int l,int r) { int k=(int)(log(r-l+1.0)/log(2.0)); if(dpmax[l][k]>dpmax[r-(1<<k)+1][k]) return index[dpmax[l][k]]; else return index[dpmax[r-(1<<k)+1][k]]; } int getmin(int l,int r) { int k=(int)(log(r-l+1.0)/log(2.0)); if(dpmin[l][k]>dpmin[r-(1<<k)+1][k]) return index[dpmin[r-(1<<k)+1][k]]; else return index[dpmin[l][k]]; } int finds(int l,int r) { int mid; int s=l; int t=r; while(s<t) { mid=(s+t)>>1; if(getmin(l,mid)==l) s=mid+1; else t=mid-1; } return t; } int main() { int n; while(scanf("%d",&n)!=EOF) { memset(dpmax,0,sizeof(dpmax)); memset(dpmin,0,sizeof(dpmin)); for(int i=0; i<n; i++) { scanf("%d",&a[i]); index[a[i]]=i; } makemax(n); makemin(n); int m=0; int s,t; for(int i=0; i<n-1; i++) { s=finds(i,n-1); t=getmax(i,s); if(t-i>m) m=t-i; } if(m==0) printf("-1\n"); else printf("%d\n",m); } return 0; }