hdu-5489(2015网络赛合肥赛区)***

题目链接

题意:给定一个长度为 N<=105 的序列 求删去连续长度为 0<=L<=N 的序列后的 LIS

思路:

假设你已经会 O(nlogn)LIS i   前面连续一段长度为 L   即[iL,i-1] 的 序列, 左边是 iL1 , 右边是 i , 求剩下的 LIS  
首先预处理出: 
f[i]:=a[i]LIS  
g[i]:=a[i]LIS
 
我们可以根据 i , 找到  [0,iL1] 之间的一个值 
其值小于 a[i] , 而其 f[i] 值是最大的, 也就是 O(n2) 求LIS的思想 
关键是如何快速确定在 [0,iL1] 中, 找到这个值 
我们利用线段树和离散化, 将 a[i] 映射到线段树上 
假设 i 位置对应的值是 x ,并且其映射到线段树上对应的下标是 y  
那么我们只要在线段树上查找 [0,y1] 之间的最大值 maxv  
maxv[i]=max{f[j],j[0,iL+1]a[j]<a[i]}  
其实就是 dp[i]:= a[i] 结尾, 删去 [iL,i1] 长度为 L LIS 的最大长度 
dp[i]=maxv[i]+g[i],ans=max{dp[i]}

注意:普通的LIS算法只能求得区间内的LIS但不能保证毕包含头或尾,

           这里利用lower_bound函数在nlogn的时间求任意前缀且包含前缀尾的LIS ( 即f[ i ] )。

           同样利用该函数求得任意后缀且包含后缀头的LIS ( 即g[ i ] )。

#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<stdio.h>
#include<math.h>
#include <string>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std;
#define eps 1e-8
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define ll long long int
#define mod 1000003
#define maxn 110000
#define maxm 10001005
int mi(int a,int b){return a<b?a:b;}
int ma(int a,int b){return a>b?a:b;}
int n,t,L,ans;
int a[maxn],aa[maxn];
int f[maxn],g[maxn],h[maxn];
int mx[maxn*4];
void update(int rt,int x,int v,int l,int r){
    if(l==r) {
        mx[rt]=ma(mx[rt],v);
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x) update(rt*2,x,v,l,mid);
    else update(rt*2+1,x,v,mid+1,r);
    mx[rt]=ma(mx[rt*2],mx[rt*2+1]);
}
int query(int rt,int lx,int rx,int l,int r){
    if(l==lx&&r==rx) return mx[rt];
    int mid=(l+r)>>1;
    if(rx<=mid) return query(rt*2,lx,rx,l,mid);
    else if(lx>mid) return query(rt*2+1,lx,rx,mid+1,r);
    else {
        int lv=query(rt*2,lx,mid,l,mid);
        int rv=query(rt*2+1,mid+1,rx,mid+1,r);
        return ma(lv,rv);
    }
}
int main()
{
    rd(t);
    int tt=0;
    while(t--){
        rd2(n,L);
        for(int i=1;i<=n;i++){
            rd(a[i]);
            aa[i]=a[i];
        }
        sort(aa+1,aa+1+n);
        int nn=unique(aa+1,aa+1+n)-aa-1;
        memset(h,0x3f,sizeof h);
        for(int i=1;i<=n;i++){//求1~i的包含a[i]的LIS!!
            int k=lower_bound(h+1,h+1+n,a[i])-h;
            f[i]=k;
            h[k]=a[i];
        }
        memset(h,0x3f,sizeof h);
        for(int i=n;i>=1;i--){//求i~n的包含a[i]的LIS!!
            int k=lower_bound(h+1,h+1+n,-a[i])-h;
            g[i]=k;
            h[k]=-a[i];
        }
        a[0]=f[0]=g[n+1]=0; a[n+1]=0x3f3f3f3f;
        memset(mx,0,sizeof mx);
        ans=0;
        for(int i=L+1;i<=n+1;i++){
            int k=lower_bound(aa+1,aa+1+nn,a[i])-aa;
            int v=query(1,0,k-1,0,nn);
            ans=ma(ans,v+g[i]);
            k=lower_bound(aa+1,aa+1+nn,a[i-L])-aa;
            if(i<=n) update(1,k,f[i-L],0,nn);
        }
        printf("Case #%d: %d\n",++tt,ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值