P8776 [蓝桥杯 2022 省 A] 最长不下降子序列 (动态规划)

测试链接:https://www.luogu.com.cn/problem/P8776

P8776 [蓝桥杯 2022 省 A] 最长不下降子序列

题目描述

给定一个长度为 \(N\) 的整数序列:\(A_{1}, A_{2}, \cdots, A_{N}\)。现在你有一次机会,将其中连续的 \(K\) 个数修改成任意一个相同值。请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。

最长不下降子序列是指序列中的一个子序列,子序列中的每个数不小于在它之前的数。

输入格式

输入第一行包含两个整数 \(N\)\(K\)

第二行包含 \(N\) 个整数 \(A_{1}, A_{2}, \cdots, A_{N}\)

输出格式

输出一行包含一个整数表示答案。

输入输出样例 #1

输入 #1

5 1
1 4 2 8 5

输出 #1

4

说明/提示

对于 \(20 \%\) 的评测用例, \(1 \leq K \leq N \leq 100\);

对于 \(30 \%\) 的评测用例, \(1 \leq K \leq N \leq 1000\);

对于 \(50 \%\) 的评测用例, \(1 \leq K \leq N \leq 10000\);

对于所有评测用例, \(1 \leq K \leq N \leq 10^{5}, 1 \leq A_{i} \leq 10^{6}\)

蓝桥杯 2022 省赛 A 组 G 题。

思路

将数组划分为三份,中间是转化成长度为k的转换区域,左边求最大不下降子序列,且结尾值小于右边区域的第一个数的大小,右边求以第一个数开头的最大不下降子序列
左边正常更新即可,右边区域需要倒序求最大不上升子序列来保证right区域求出正确的最大不下降子序列

题解

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
int t,n;
int k;
int a[N],r[N],endss[N];
int ans;

int f2(int len,int num)
{
    int l=0,r=len-1,ans=-1;
    while(l<=r)
    {
        int m = (l+r)/2;
        if(endss[m]>num)
        {
            ans = m;
            r = m-1;
        }
        else l = m+1;
    }
    return ans;
}

int f1(int len,int num)
{
    int l=0,r=len-1,ans=-1;
    while(l<=r)
    {
        int m = (l+r)/2;
        if(endss[m]<num)
        {
            ans = m;
            r = m-1;
        }
        else l = m+1;
    }
    return ans;
}


void right()
{
    int len = 0;
    for(int i=n-1;i>=0;i--)
    {
        int find = f1(len,a[i]);
        if(find==-1)
        {
            endss[len++] = a[i];
            r[i] = len;
        }
        else 
        {
            endss[find]=a[i];
            r[i]=find+1;
        }
    }
}


int compute()
{
    right();
    int len = 0;
    int ans = 0;
    for(int i=0,j=k,left;j<n;i++,j++)
    {
        int find = f2(len,a[j]);
        left = find==-1?len:find;
        ans = max(ans,left+k+r[j]);
        find = f2(len,a[i]);
        if(find==-1)endss[len++] = a[i];
        else endss[find] = a[i];
    }
    ans = max(ans,len+k);
    return ans;
}


int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>k;
    
    for(int i=0;i<n;i++)cin>>a[i];
    if(k>=n)cout<<n<<endl;
    else 
    {
        cout<<compute()<<endl;
    }
    return 0;
}
posted @ 2025-08-01 15:24  屈臣  阅读(14)  评论(0)    收藏  举报