Description
一个队伍中站在最前面的人是最矮的,并且站在最后面的人是最高的,那么这个队伍是和谐的。
现在 N个人站成一队,第i个人身高为 ℎi,对于队伍中的一个区间[L,R],如果第 L 个人的高度小于[L+1,R]所有人的高度,并 且第R个人的高度大于[L,R−1]所有人的高度,那么[L,R]称 为一个和谐区间。
现在给出队伍的情况,求最长的和谐区间的长度。
Input
第一行,一个数N,接下来N行,每行一个数 ℎi
Output
一个数,表示最长和谐区间的长度
Sample Input
5
1
2
3
4
1
Sample Output
4
Hint
N≤100000,1≤ℎi≤2^31
【分析】
说说我的思路
1.先缩点。
就是说把一坨一样的缩成一个。为什么呢,因为方便,而且会快些。
正确性:考虑这样一组数据N为10:1 1 1 2 2 1 1 7 9 9
答案是完全等价:1 2 17 9
所以缩点是没问题的
2.用单调队列(这里应该是单调栈)来算出每个位置右边第一个小于等于他的数的位置。然后r[i]就存那个位置。
3.对于每个i求出i+1~r[i]-1的最大值出现在哪个地方,设为loc[i];
最后答案就是max(loc[i]+1-i)(1<=i<=N)
注意细节的处理,第2步最后要让0进栈,才能把所有元素出栈,(我写的严格单调递增的单调栈);
至于第3步,RMQ问题,随便线段树啊,ST啊就搞掉了。当然对于查询次数为10w级,还是推荐用ST节约时间(空间当然会增加)。
【代码】
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int N,v[100005],Q[100005],Qr[100005],r[100005];
int Fmax[100005][20],Fr[100005][20];
void _in(int &x)
{
char t=getchar();
while(t<'0'||'9'<t) t=getchar();
for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _init()
{
_in(N);
int j=1;
for(int i=1;i<=N;i++,j++)
{
_in(v[j]);
if(v[j]==v[j-1])
j--;
}
N=--j;
v[N+1]=0;
for(int i=1;i<=N;i++)
{
Fmax[i][0]=v[i];
Fr[i][0]=i;
}
}
void _solve()
{
int head=1,tail=2;
Q[1]=v[1];
Qr[1]=1;
for(int i=2;i<=N+1;i++)
{
while(v[i]<=Q[tail-1]&&tail>head)
{
tail--;
r[Qr[tail]]=i-1;
}
Qr[tail]=i;
Q[tail++]=v[i];
}
for(int j=1;j<=floor(log(N)/log(2));j++)
for(int i=1;i<=(N+1-(1<<j));i++)
{
Fmax[i][j]=max(Fmax[i][j-1],Fmax[i+(1<<(j-1))][j-1]);
if(Fmax[i][j]==Fmax[i][j-1])
Fr[i][j]=Fr[i][j-1];
else
Fr[i][j]=Fr[i+(1<<(j-1))][j-1];
}
int a,b,k,ans=0,Qmax,Qr;
for(int i=1;i<=N;i++)
{
a=i+1;
b=r[i];
if(a>=b) continue;
k=floor(log(b+1-a)/log(2));
Qmax=max(Fmax[a][k],Fmax[b+1-(1<<k)][k]);
if(Qmax==Fmax[a][k])
Qr=Fr[a][k];
else
Qr=Fr[b+1-(1<<k)][k];
ans=max(ans,Qr+1-i);
}
printf("%d\n",ans);
}
int main()
{
_init();
_solve();
return 0;
}