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;
}

浙公网安备 33010602011771号