NKOJ P2386 [HZOI] 排队【线段树】【单调栈】

题目描述

一个队伍中站在最前面的人是最矮的,并且站在最后面的人是最高的,那么这个队伍是和谐的。
现在 N 个人站成一队,第 i 个人身高为 h i ,对于队伍中的一个区间 [ L , R ] ,如果第 L 个人的高度小于 [ L + 1 , R ] 所有人的高度,并 且第 R 个人的高度大于 [ L , R 1 ] 所有人的高度,那么 [ L , R ] 称 为一个和谐区间。
现在给出队伍的情况,求最长的和谐区间的长度。

输入格式

第一行,一个数 N ,接下来 N 行,每行一个数 h i

输出格式

一个数,表示最长和谐区间的长度

样例输入

5
1 2 3 4 1

样例输出

4


题外话:

感觉这道题还是很好…最开始我把题目读错了(也不能叫做读错题目应该是自己有一点想当然),因为题目说了和谐区间的定义是首位分别为区间最小值与区间最大值,然后我就想成和谐区间的定义是这段区间为严格单增序列,后来还是和 C i o C i o A C 代码对拍了才发现这个错误。

改悔。超级不应该。


正经话:

由于和谐区间的首项小于该区间的所有数,那么我们不妨先找到第 i 个数的右边第一个不大于它的数(记这个数的下标为 R i ),那么很显然,此时我们只需要找到区间 [ i , R [ i ] ] 的最大数的下标即可,(记这个最大数的下标为 M a x i ),那么以第 i 个数为首项的最长的和谐区间的长度即为(记 L e n i 为以第 i 个数为首项的最长的和谐区间的长度):

L e n i = M a x i + 1 i

最后的答案就应当为:
A n s = m a x L e n i

那么问题来了:
1. 我们应该如何得到 R [ ] 呢。由于 R [ ] 记录的是第 i 个数的右边第一个不大于它的数,也就是(记原数组为 A [ ] A [ i ] A [ R [ i ] ] ,也就是说它是满足一个单调性的,我们就可以用单调栈统计出 R [ ]
2. 我们应该如何得到 M a x i 呢。由于得到 M a x i 的过程就相当于处理静态区间的最大值的过程,有很多种数据结构可以实现( S T 表,树状数组,线段树等等)。但是要注意:我们此处的 M a x i 是下标,而平常的区间最大值返回的是值,但这也没有多复杂。
3. 如果查询区间内最大值有多个,我们应该取哪一个下标呢?我最开始想的是取最右边的那个下标,因为这样的话我们得到的区间长度就会更长,似乎这样想没有问题?但是问题大了,如果有多个最大值而我们取了最右边的那一个,那么这个区间就不符合和谐区间的定义了,因为这个最大值是要作为一个区间的末项的,而末项是大于所有区间内的数的。

参考代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define SG string
#define DB double
using namespace std;
const int Max=1e5+5;
struct Node{
    int X,Y,Num,_Max;
}Tree[Max<<3];
int N,Ans,A[Max],R[Max];
int Head=1,Tail=1,Stack_Id[Max],Stack_Num[Max];
inline int Read(){
    int X=0;char CH=getchar();bool F=0;
    while(CH>'9'||CH<'0'){if(CH=='-')F=1;CH=getchar();}
    while(CH>='0'&&CH<='9'){X=(X<<1)+(X<<3)+CH-'0';CH=getchar();}
    return F?-X:X;
}
inline void Write(int X){
    if(X<0)X=-X,putchar('-');
    if(X>9)Write(X/10);
    putchar(X%10+48);
}
void Init(){
    int I,J;N=Read();
    for(I=1;I<=N;I++){
        A[I]=Read();
    }
}
void _Stack(){
    int I,J;
    Stack_Id[1]=1;
    Stack_Num[1]=A[1];
    for(I=2;I<=N+1;I++){
        while(Head<=Tail&&A[I]<=Stack_Num[Tail]){
            R[Stack_Id[Tail]]=I-1;Tail--;
        }
        Tail++;
        Stack_Id[Tail]=I;
        Stack_Num[Tail]=A[I];
    }
}
void MakeTree(int P,int X,int Y){
    Tree[P].X=X;Tree[P].Y=Y;
    if(X<Y){
        MakeTree(P<<1,X,X+Y>>1);
        MakeTree(P<<1|1,(X+Y>>1)+1,Y);
        if(Tree[P<<1]._Max>=Tree[P<<1|1]._Max){
            Tree[P].Num=Tree[P<<1].Num;
            Tree[P]._Max=Tree[P<<1]._Max;
        } else {
            Tree[P].Num=Tree[P<<1|1].Num;
            Tree[P]._Max=Tree[P<<1|1]._Max;
        }
    }
    if(X==Y){
        Tree[P].Num=X;
        Tree[P]._Max=A[X];
    }
}
int Get_Max_Num(int P,int X,int Y){
    int I,J,K;
    if(X<=Tree[P].X&&Tree[P].Y<=Y){
        return Tree[P].Num;
    }
    int L_Max_Num=0,R_Max_Num=0,Mid=Tree[P].X+Tree[P].Y>>1;
    if(X<=Mid&&Tree[P].X<=Y){
        L_Max_Num=Get_Max_Num(P<<1,X,Y);
    }
    if(Mid<Y&&X<=Tree[P].Y){
        R_Max_Num=Get_Max_Num(P<<1|1,X,Y);
    }
    if(A[L_Max_Num]>=A[R_Max_Num]){
        return L_Max_Num;
    } else {
        return R_Max_Num;
    }
}
void __Solve(){
    int I;
    MakeTree(1,1,N);
    for(I=1;I<=N;I++){
        if(I+1>=R[I]){
            continue;
        }
        Ans=max(Ans,Get_Max_Num(1,I,R[I])+1-I);
    }
    Write(Ans);
}
int main(){
    int I,J,K;
    Init();
    _Stack();
    __Solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81140664