您的位置:首页 > 其它

【GDOI模拟】奇妙的数列

2016-04-10 00:08 225 查看

Description



Solution

题意就是要求最长的一段区间,中间的最小值不比左端点小,中间的最大值不比右端点大。

第一眼可以用线段树

维护最大值和最小值。

时间复杂度O(n(logn)2)

第二眼就想到O(n)

对于i来说因为只用找右边第一个比a[i]小的数,和左边第一个比a[i]大的数,就可以了。

维护这两个值O(n)用f和g数组预处理一下就好了。

f[i]表示右边第一个比a[i]小的数的左边那个数。

g[i]表示左边第一个比a[i]大的数的右边那个数。

如果找出了边界,就存边界就好了。

然后很明显,对于i来说,i到f[i]这段区间是合法的,因为这些数都不小于他。那么我们只用再在这个区间内找一个数j,他的g[j]是不大于i的就好了,这说明g[j]到j这段区间的数都不大于a[j],然后再要求j最大,到这用g一直跳到覆盖i为止就好了。

因为每个数只会被比较一次,所以时间复杂度O(n)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=10000007;
inline int read(){
char ch=getchar();
int data=0;
while(ch<'0'||ch>'9'){
ch=getchar();
}
do{
data=data*10+ch-'0';
ch=getchar();
}while(ch>='0'&&ch<='9');
return data;
}
int i,j,k,l,t,n,m,ans,f[maxn],g[maxn];
int a[maxn];
int main(){
n=read();
fo(i,1,n){
a[i]=read();
}
fod(i,n,1){
f[i]=i;
while(f[i]+1<=n&&a[f[i]+1]>=a[i])f[i]=f[f[i]+1];
}
fo(i,1,n){
g[i]=i;
while(g[i]-1>=1&&a[g[i]-1]<=a[i])g[i]=g[g[i]-1];
}
fo(i,1,n){
t=f[i];
while(g[t]>i)t=g[t]-1;
ans=max(t-i+1,ans);
}
printf("%d\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: