HDU - 3564 Another LIS(线段树(最值) + 线段树解决插队问题)

 

做这个题之前,建议先做 POJ 2828.   附题解 POJ 2828题解

 

Another LIS

There is a sequence firstly empty. We begin to add number from 1 to N to the sequence, and every time we just add a single number to the sequence at a specific position. Now, we want to know length of the LIS (Longest Increasing Subsequence) after every time's add.

Input

An integer T (T <= 10), indicating there are T test cases. 
For every test case, an integer N (1 <= N <= 100000) comes first, then there are N numbers, the k-th number Xk means that we add number k at position Xk (0 <= Xk <= k-1).See hint for more details. 

Output

For the k-th test case, first output "Case #k:" in a separate line, then followed N lines indicating the answer. Output a blank line after every test case.

Sample Input

1
3
0 0 2

Sample Output

Case #1:
1
1
2

Hint

In the sample, we add three numbers to the sequence, and form three sequences.
a. 1
b. 2 1
c. 2 1 3

题意:有一个起初是空的序列。我们开始在序列中添加从1到N的数字,每次我们只在特定位置向序列添加一个数字。现在,我们想知道每次添加后LIS(最长上升子序列)的长度。

思路:首先就是如 POJ 2828 一样,解决插队完成后的序列是什么  这个问题。 接着需要解决的问题才是 每次添加后的LIS 长度是多少

对于每次添加后的LIS 长度,可以这样解决:

如序列 4 1 3 2,可以按这个序列建树,维护的是一个最大值,表示以 a[i] 结尾的 LIS 长度,如 3 :表示序列以 3 结尾的 LIS 长度 。查询的时候就应该查询的是 区间 (1, a[i] - 1) 的最大值,然后 + 1

这样就可以得到 以 每个数为结尾的 LIS 长度

由于 插入的时候是按顺序添加 1 ~ n 的数,所以最后 再在 以 a[i] 与 a[i] - 1 两个为结尾的LIS中取个max 就是答案。

AC代码:

#include<bits/stdc++.h>
#define debug(x) cout << "[" << #x <<": " << (x) <<"]"<< endl
#define pii pair<int,int>
#define clr(a,b) memset((a),b,sizeof(a))
#define rep(i,a,b) for(int i = a;i < b;i ++)
#define pb push_back
#define MP make_pair
#define LL long long
#define ull unsigned LL
#define ls i << 1
#define rs (i << 1) + 1
#define INT(t) int t; scanf("%d",&t)

using namespace std;

const int maxn = 1e5 + 10;
int a[maxn],wz[maxn];
int Left[maxn << 2];

void build(int l,int r,int i){
    if(l == r)
        Left[i] = 1;
    else {
        int mid = (l + r) >> 1;
        build(l,mid,ls);
        build(mid + 1,r,rs);
        Left[i] = Left[ls] + Left[rs];
    }
}

void query(int l,int r,int i,int pos,int val){
    if(l == r){
        -- Left[i];
        wz[l] = val;
        return ;
    }
    int mid = (l + r) >> 1,ans;
    if(Left[ls] >= pos) query(l,mid,ls,pos,val);
    else query(mid + 1,r,rs,pos - Left[ls],val);
    Left[i] = Left[ls] + Left[rs];
}

int LIS[maxn << 2];
int ans[maxn];

void updateLIS(int l,int r,int pos,int val,int i){
    if(l == r){
        LIS[i] = val;
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) updateLIS(l,mid,pos,val,ls);
    if(pos > mid) updateLIS(mid + 1,r,pos,val,rs);
    LIS[i] = max(LIS[ls],LIS[rs]);
}

int queryLIS(int l,int r,int ql,int qr,int i){
    if(ql > qr) return 0;
    if(ql <= l && r <= qr){
        return LIS[i];
    }
    int mid = (l + r) >> 1;
    int ans = -1;
    if(ql <= mid) ans = max(ans,queryLIS(l,mid,ql,qr,ls));
    if(qr > mid) ans = max(ans,queryLIS(mid + 1,r,ql,qr,rs));
    return ans;
}

int main() {
    int t; scanf("%d",&t);
    int Cas = 1;
    while(t --){
        int n; scanf("%d",&n);
        for(int i = 1;i <= n;++ i)
            scanf("%d",&a[i]);
        build(1,n,1);
        for(int i = n;i > 0;-- i){
            query(1,n,1,a[i] + 1,i);
        }
        clr(LIS,0);
        clr(ans,0);
        for(int i = 1;i <= n;++ i){
            int tmp = queryLIS(1,n,1,wz[i] - 1,1) + 1;
            ans[wz[i]] = max(tmp,ans[wz[i] - 1]);
            updateLIS(1,n,wz[i],tmp,1);
        }
        printf("Case #%d:\n",Cas ++);
        for(int i = 1;i <= n;++ i){
            ans[i] = max(ans[i],ans[i - 1]);
            printf("%d\n",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值