HDU 5489 Removed Interval (离散化数据建树+线段树+滑动窗口)*

Removed Interval

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2223    Accepted Submission(s): 700


 

Problem Description

Given a sequence of numbers A=a1,a2,…,aN, a subsequence b1,b2,…,bk of A is referred as increasing if b1<b2<…<bk. LY has just learned how to find the longest increasing subsequence (LIS).
Now that he has to select L consecutive numbers and remove them from A for some mysterious reasons. He can choose arbitrary starting position of the selected interval so that the length of the LIS of the remaining numbers is maximized. Can you help him with this problem?

 

 

Input

The first line of input contains a number T indicating the number of test cases (T≤100).
For each test case, the first line consists of two numbers N and L as described above (1≤N≤100000,0≤LN). The second line consists of N integers indicating the sequence. The absolute value of the numbers is no greater than 109.
The sum of N over all test cases will not exceed 500000.

 

 

Output

For each test case, output a single line consisting of “Case #X: Y”. X is the test case number starting from 1. Y is the maximum length of LIS after removing the interval.

 

 

Sample Input

 

2 5 2 1 2 3 4 5 5 3 5 4 3 2 1

 

 

Sample Output

 

Case #1: 3 Case #2: 1

 

 

Source

2015 ACM/ICPC Asia Regional Hefei Online

 

 

Recommend

wange2014   |   We have carefully selected several similar problems for you:  6408 6407 6406 6405 6404 

 

 

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)

#define root l,r,rt
#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int  maxn =5e5+5;
/*
题目大意:给定一个序列,要求其删除连续k个后的最长
LIS长度。

首先看题解才知道离散化数据的概念,
也是张了见识了,离散化数据在线段树构建中很常用,
就是把不对数的绝对大小进行建树,而对数的大小序号序列进行建树。

在滑动窗口滑动的时候,每次都不断的更新左断扫过的数据,更新到ST中,
更新的位置是离散化的位置,这样每次查询的时候就可以直接引用全局最大值,
因为破坏查询位置答案的数字还没出现,或者是更新不到查询区间中,究其本质
还是大小关系,查询要求比目标值小的最优值,
那么线段树维护的是最大值即可。

离散化的具体步骤,很简单,就是原数组排序然后记录序号。


*/

int n,k,seq[maxn],tmp[maxn];
int dp[maxn][2];///0代表以当前位为结尾的最长上升子序列,1代表以当前位为开头的最长序列
///ST结构
int st[maxn<<2];
void pushup(int rt) { st[rt] = max(st[rt<<1] , st[rt<<1|1]); }
void build(lrt)
{
    if(l==r){ st[rt]=0;return ; }
    int mid=l+r>>1;
    build(lson),build(rson),pushup(rt);
}
int query(lrt,int L,int R)///返回小于x的最大长度
{
    if(L<=l&&r<=R) return st[rt];
    int mid=l+r>>1,ans=0;
    if(L<=mid) ans=max(query(lson,L,R),ans);
    if(mid<R) ans=max(query(rson,L,R),ans);
    return ans;
}
void update(lrt,int pos,int x)
{
    if(l==r) {st[rt]=x;return;}
    int mid=l+r>>1;
    if(pos<=mid) update(lson,pos,x);
    if(mid<pos) update(rson,pos,x);
    pushup(rt);
}

void init()
{
    int tp[maxn];///借用的暂时的数组

    memset(dp,0,sizeof(dp));
    memset(tp,0x7f,sizeof(tp));///数据范围要注意
    for(int i=1;i<=n;i++)
    {
        int k=lower_bound(tp+1,tp+1+n,seq[i])-tp;
        dp[i][0]=k;
        tp[k]=seq[i];
    }

    memset(tp,0x7f,sizeof(tp));///每个数大小题目中达到10^9
    for(int i=n;i;i--)
    {
        int k=lower_bound(tp+1,tp+1+n,-seq[i])-tp;
        dp[i][1]=k;
        tp[k]=-seq[i];
    }
    ///初始化dp数组
}

int main()
{
    int t;scanf("%d",&t);
    for(int ca=1;ca<=t;ca++)
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&seq[i]),tmp[i]=seq[i];

        map<int,int> mp;int cnt=0;
        sort(tmp+1,tmp+n+1);///离散化,类似于树状数组常用技巧的操作
        for(int i=1;i<=n;i++)   if(mp[tmp[i]]==0) mp[tmp[i]]=++cnt;

        init( );
        build(0,n,1);///用开头单调上升序列维护线段树

        ///for(int i=1;i<=n;i++) cout<<dp[i][0]<<" ";puts("");
        ///for(int i=1;i<=n;i++) cout<<dp[i][1]<<" ";puts("");

        int ans=0;
        for(int i=1;i<=n;i++)
        {
            int l=i,r=i+k-1;
            if(r>n)break;
            if(l==1) ans=max(ans,dp[r+1][1]);
            else if(r==n) ans=max(ans,query(1,n,1,1,n));
            else   ans=max(ans,dp[r+1][1]+query(1,n,1,1,mp[seq[r+1]]));
            update(1,n,1,mp[seq[i]],dp[i][0]);
        }
        printf("Case #%d: %d\n",ca,ans);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值