hdu4521 小明序列 (线段树 + DP)

时间:2023-01-14 19:31:38

题意:题目的意思很简单,给定一个序列,求最大递增子序列的长度,同时相邻俩项下标的差值必须大于d

分析:就题意而已,只是在最长递增子序列的加了一个限制条件,沿用最长递增子序列的思路,则dp[i] = max(dp[i], dp[j] + 1), (i - j > d && a[i] > a[j])

从状态转移方程可以看出,问题的关键在于维护区间(0,i - d - 1)  最大的dp值,且对应值小于a[i]

我们换一种角度,用a[i]的值作为下标,保证当前区间(0, a[i] - 1) 中的dp值都满足 (i - j > d) , 那么dp[i] 就等于区间(0, a[i] - 1) 的最大值, 这个就可以用线段树来解决了

hdu4521 小明序列 (线段树 + DP)hdu4521 小明序列 (线段树 + DP)hdu4521
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

using namespace std;

const int N = 100000 + 10;

struct node
{
    int l, r, max;
}p[N * 3];

int a[N], d, n;
int dp[N];

void build(int s, int t, int k)
{
    p[k].l = s;
    p[k].r = t;
    p[k].max = 0;
    if( s == t ) return ;
    int mid = (s + t) >> 1, kl = k << 1, kr = kl + 1;
    build(s, mid, kl);
    build(mid + 1, t, kr);
}

void insert(int k, int s, int val)
{
    if( p[k].l == p[k].r && p[k].l == s)
    {
        p[k].max = max(p[k].max, val);
        return ;
    }
    int mid = (p[k].l + p[k].r) >> 1, kl = k << 1, kr = kl + 1;
    if( s <= mid) insert(kl, s, val);
    else insert(kr, s, val);
    p[k].max = max(p[kl].max, p[kr].max); 
}

int query(int k, int s, int t)
{
    if(s <= p[k].l && t >= p[k].r)
    {
        return p[k].max;
    }

    int mid = (p[k].l + p[k].r) >> 1, kl = k << 1, kr = kl + 1;
    int a = 0, b = 0;
    if(s <= mid) a = query(kl, s, t);
    if(t > mid) b = query(kr, s, t);
    return max(a, b);
}

int main()
{
    while(scanf("%d %d", &n, &d) == 2)
    {
        int maxa = 0;
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", a + i);
            ++a[i];
            maxa = max(maxa, a[i]);
        }
        memset(dp, 0, sizeof(dp));
        build(0, maxa, 1);
        int ans = 1;
        for(int i = 1; i <= n; ++i)
        {
            int tmp = query(1, 0, a[i] - 1);
            dp[i] = max(dp[i], tmp + 1);
            ans = max(ans, dp[i]);
            if(i - d > 0) insert(1, a[i - d], dp[i - d]);//在线段树中插入满足下标满足的DP值
        }
        printf("%d\n",ans);
    }
    return 0;
}