HDOJ 4604 - Deque O(nlogn)的最长非升(非降)

   首先看几个可以完全放完的方案:     4 3 2 1 5 : (4),(3 4),(2 3 4),(1 2 3 4),(1 2 3 4 5)

                                                           3 4 2 5 1 :(3),(3 4),(2 3 4),(2 3 4 5),(1 2 3 4 5)

   可见需要找的是一个非降序列和非升序列..他们没有重合的部分..长度之和最大...因为对于非降的..从右进..非升的从左进..又保证了不重合..那么必定从右进的所有数大于等于从左进的所有数...

   由于题目没有给出每个数的大小...先离散化..确定每个数在数列中是第几大....找最长非降和最长非升必须用O(nlogn)的算法...这个算法不在累述...在找每个数的最长非升和非降的时候跟新离散化的这个数的最长非降和最长非升..最后..找到每个数k的最长非降+(k~n)的最长非升最大值..这个过程可以O(n)完成..

   比如  4 3 2 1 5 ....L[0]记录每个数的长非降..L[1]记录每个数的最长非升...都是从后往前...不如把原数列反过来看..5 1 2 3 4

                                原值  1  2  3  4  5

           离散化后值的顺序  1  2  3  4  5

                                 L[0]   1  2  3  4  1

                                 L[1]   2  2  2  2  1        这其中L[0][i]+max(L[1][k]) < k>i > 的最大值有L[0][3]+L[1][4]=5,L[0][4]+L[1][5]..也就是< (3,2,1),(4,5)>与<(4,3,2,1),(5)>

  又比如说3 3 3 3 4 4 3 3  反过来看 3 3 4 4 3 3 3 3...原数列只有两个数值..离散化后就只有两个值了...

                                原值   3  4                     

           离散化后值的顺序   1  2 

                                 L[0]    6  4

                                 L[1]    6  2                   这其中L[0][i]+max(L[1][k]) < k>i > 的最大值有L[0][1]+L[1][2]=8,也就是<(3,3,3,3,3,3),(4,4)>

  最后一个问题..如果已经得到了L[0],L[1]..如何在O(n)的时间内找出L[0][i]+max(L[1][k]) < k>i >的最大值.

  对于每个L[0][i]每次要找的是max(L[1][k]) < k>i >,那么不妨将扫描顺序从i最大开始..边更新答案..边更新最长的可用非升...

  最后统计时不这么做..用个线段树来维护也行(找区间最大值).我开始是用线段树维护的..写崩了..但一定是可行的...

 

Program:

#include<iostream>
#include<stack>
#include<queue>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<cmath>
#define ll long long
#define oo 1000000007
#define MAXN 100010
using namespace std;
int n,a[MAXN],num,X[MAXN],b[MAXN],M[MAXN],L[2][MAXN],H[2][MAXN],m[2];  
int turn(int x)
{
      int l,r,mid;
      l=0,r=num+1;
      while (r-l>1)
      {
            mid=(l+r)>>1;
            if (X[mid]<=x) l=mid;
               else r=mid;
      }
      return l;
}
void find1()
{
      int i,l,r,mid,x;
      m[0]=0;
      for (i=n;i>=1;i--)
      {
            x=a[i];
            l=0; r=m[0]+1;
            while (r-l>1)
            {
                  mid=(l+r)>>1;
                  if (H[0][mid]<=x) l=mid;  
                      else r=mid;
            }
            if (r>m[0]) m[0]=r,H[0][r]=a[i];
               else H[0][r]=min(H[0][r],a[i]);
            x=turn(x);
            L[0][x]=max(L[0][x],r);
      }
      return;
}
void find2()
{
      int i,l,r,mid,x;
      m[1]=0;
      for (i=n;i>=1;i--)
      {
            x=a[i];
            l=0; r=m[1]+1;
            while (r-l>1)
            {
                  mid=(l+r)>>1;
                  if (H[1][mid]>=x) l=mid;  
                      else r=mid;
            }
            if (r>m[1]) m[1]=r,H[1][r]=a[i];
               else H[1][r]=max(H[1][r],a[i]);
            x=turn(x);
            L[1][x]=max(L[1][x],r);
      }
      return;
} 
int main()
{   
      int Case,i,ans,MM; 
      scanf("%d",&Case);
      while (Case--)
      {
             scanf("%d",&n);
             for (i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];  
             sort(b+1,b+1+n),b[0]=-oo,num=0;
             for (i=1;i<=n;i++) 
                if (b[i]!=b[i-1]) X[++num]=b[i]; // 由于不知道每个数的范围..相当于离散化了.
             memset(L,0,sizeof(L));
             find1();
             find2();              
             ans=MM=0; 
             for (i=num;i>=1;i--)
             {
                   ans=max(ans,L[0][i]+MM); 
                   MM=max(MM,L[1][i]); //更新可用的最长非升 
             }
             printf("%d\n",ans);
      }
      return 0;
}
/*
5
7
1 2 3 4 5 6 7
5
4 3 2 1 5
5
5 4 1 2 3
8
3 3 3 3 4 4 3 3
5
1 1 1 1 1 
*/

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值