首页 > 其他分享 >最长不下降子序列

最长不下降子序列

时间:2023-04-25 17:45:19浏览次数:42  
标签:修改 int 下降 leq 序列 最长

最长不下降子序列

给定一个长度为 $N$ 的整数序列:$A_1,A_2, \ldots ,A_N$。

现在你有一次机会,将其中连续的 $K$ 个数修改成任意一个相同值。

请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。

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

输入格式

输入第一行包含两个整数 $N$ 和 $K$。

第二行包含 $N$ 个整数 $A_1,A_2, \ldots ,A_N$。

输出格式

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

数据范围

对于 $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}$。

输入样例:

5 1
1 4 2 8 5

输出样例:

4

 

解题思路

  假设我们选取长度为$k$的区间$[l,r]$进行修改,然后在$[1,l-1]$中先选择一个不下降子序列,假设以$a_i$结尾,很明显为了使得不下降子序列最长,可以直接把$i \in [l,r]$内的数都修改成$a_i$。然后再从$j \in [r+1,n]$中找到一个以$a_j$开头的不下降子序列,其中要满足$a_j \geq a_i$,这样才能保证整个子序列是不下降的。

  可以发现当固定了要修改的区间后整个序列被分成了前后两个部分,而现在要从这两部分中选择相应的$a_i$与$a_j$使得不下降子序列最长。由于我们需要用到以$a_i$为结尾的最长不下降子序列,和以$a_j$为开头的最长不下降子序列,因此要用线段树优化来求最长不下降子序列。

  定义$f(i)$表示所有以第$i$个数结尾的不下降子序列中长度的最大值,$g(i)$表示所有以第$i$个数开头的不下降子序列中长度的最大值。状态转移方程就是$$f(i) = \max_{\begin{array}{center} 1 \leq j < i \\ a_j \leq a_i \end{array}} \left\{ f(j) \right\} + 1$$$$g(i) = \max_{\begin{array}{center} i < j \leq n \\ a_j \geq a_i \end{array}} \left\{ g(j) \right\} + 1$$

  当然还需要用到权值线段树去优化,这样才能做到$O(n \log{M})$的时间复杂度,具体做法可以参考:线段树优化最长上升子序列问题

  考虑所有修改后的序列,我们可以把所有不下降子序列所构成的集合按照在前部分(假设修改区间为$[l,r]$,前部分指$[1,l-1]$)中不下降子序列以哪个数结尾来进行划分。因此可以划分的类别有:没有前部分,即修改的区间是$[1,k]$;以第$1$个数结尾;以第$2$个数结尾;一直到以第$n-k$个数结尾,即修改的区间是$[n-k+1,n]$。

  对于以第$i$个数结尾的所有不下降子序列中,最大长度就是$f(i) + k + \max\limits_{\begin{array}{center} r < j \leq n \\ a_j \geq a_i \end{array}} \left\{ g(j) \right\}$,其中$r$是修改区间的右端点,取值范围是$r \in [i+k, n]$,这个修改区间可以是在$i$右部的任意一个长度为$k$的连续区间。首先$a_i$固定了,因此前部分最长的不下降子序列就是$f(i)$,然后再加上整个修改区间的长度,最后再加上在后部分满足开头至少为$a_i$的最长的不下降子序列。

  对于没有前部分的情况,最大长度就是$k+ \max\limits_{k < j \leq n} \{ g(i) \}$。

  因此我们可从右往左枚举修改区间的右端点$i$,开一个权值线段树来维护右部分的$g(i)$,这样就可以维护后缀最大值,查询右部分的最大值时直接在线段树中找到满足值大于等于$a_{i-m}$中最大的$g(i)$。

  AC代码如下,时间复杂度为$O(n \log{M})$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1e5 + 10, M = 1e6 + 10;
 5 
 6 int a[N];
 7 int f[N], g[N];
 8 struct Node {
 9     int l, r, maxv;
10 }tr[M * 4];
11 
12 void build(int u, int l, int r) {
13     if (l == r) {
14         tr[u] = {l, r, 0};
15     }
16     else {
17         int mid = l + r >> 1;
18         build(u << 1, l, mid);
19         build(u << 1 | 1, mid + 1, r);
20         tr[u] = {l, r, 0};
21     }
22 }
23 
24 void modify(int u, int x, int c) {
25     if (tr[u].l == tr[u].r) {
26         tr[u].maxv = c;
27     }
28     else {
29         if (x <= tr[u].l + tr[u].r >> 1) modify(u << 1, x, c);
30         else modify(u << 1 | 1, x, c);
31         tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
32     }
33 }
34 
35 int query(int u, int l, int r) {
36     if (tr[u].l >= l && tr[u].r <= r) return tr[u].maxv;
37     int mid = tr[u].l + tr[u].r >> 1, ret = 0;
38     if (l <= mid) ret = query(u << 1, l, r);
39     if (r >= mid + 1) ret = max(ret, query(u << 1 | 1, l, r));
40     return ret;
41 }
42 
43 int main() {
44     int n, m;
45     scanf("%d %d", &n, &m);
46     for (int i = 1; i <= n; i++) {
47         scanf("%d", a + i);
48     }
49     build(1, 1, M - 1);
50     for (int i = 1; i <= n; i++) {  // 求以a[i]结尾的最长不下降子序列
51         f[i] = query(1, 1, a[i]) + 1;
52         modify(1, a[i], f[i]);
53     }
54     build(1, 1, M - 1);
55     for (int i = n; i; i--) {   // 求以a[i]开头的最长不下降子序列
56         g[i] = query(1, a[i], M - 1) + 1;
57         modify(1, a[i], g[i]);
58     }
59     build(1, 1, M - 1);
60     int ret = 0;
61     for (int i = n; i >= m; i--) {
62         // 边界是i=m,此时修改的区间是[1~m],对应不存在前部分的情况。而此时f[0]=a[0]=0,因此用同样的方法得到的结果也是正确的
63         ret = max(ret, f[i - m] + m + query(1, a[i - m], M - 1));
64         modify(1, a[i], g[i]);
65     }
66     printf("%d", ret);
67     
68     return 0;
69 }

 

参考资料

  AcWing 4648. 最长不下降子序列:https://www.acwing.com/solution/content/143797/

标签:修改,int,下降,leq,序列,最长
From: https://www.cnblogs.com/onlyblues/p/17353338.html

相关文章

  • Java序列化和反序列化
    目录一、序列化和反序列化二、Java序列化演示三、反序列化漏洞一、序列化和反序列化1、含义​ 序列化就是内存中的对象写入到IO流中,保存的格式可以是二进制或者文本内容。反序列化就是IO流还原成对象。2、用途(1)传输网络对象(2)保存Session二、Java序列化演示1、序列化java......
  • C# 序列化与反序列化XML文件
    1//整理输出数据2List<RowData>lisOutputData=newList<RowData>();3foreach(varitemindicAssist.Keys)4{5stringkey=item+dicAssist[item];6foreach(varitmindicRowNumber[key])7{8lisOutputData.Add(dicR......
  • 23-4-24--子序列--最长连续递增子序列
    给定一个顺序存储的线性表,请设计一个算法查找该线性表中最长的连续递增子序列。例如,(1,9,2,5,7,3,4,6,8,0)中最长的递增子序列为(3,4,6,8)。输入格式:输入第1行给出正整数n(≤105);第2行给出n个整数,其间以空格分隔。输出格式:在一行中输出第一次出现的最长连续递增子序列,数字......
  • java反序列化(五) JNDI注入
    JNDI注入前置知识JNDIJNDI(JavaNamingandDirectoryInterface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。可以通过字符串来锁定一个对象JNDI支持的服务主要有以下几种:RMI(JAVA远程方法调用)LDAP(轻量级目录访问协......
  • java反序列化(五) JNDI注入
    JNDI注入前置知识JNDIJNDI(JavaNamingandDirectoryInterface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口。可以通过字符串来锁定一个对象JNDI支持的服务主要有以下几种:RMI(JAVA远程方法调用)LDAP(轻量级目录访问协......
  • 唯品会季报图解:运营利润环比下降21.5%
    腾讯科技唯品会 (纽交所证券代码:VIPS)今日发布财报。财报显示,唯品会第二季度净营收8.294亿美元,较上年同期增136.1%;归属于唯品会净利润2640万美元,较上年同期增长192.1%。唯品会董事长兼首席执行官沈亚称,乐峰网第二季度贡献近200万的订单和超过130万的活跃用户。随着移动日益成为......
  • Python学习笔记--json序列化时间报错-改源码
    问题:转换时间报错执行代码为:importjsonfromdatetimeimportdate,datetimed={"time1":date.today(),"time2":datetime.today()}res=json.dumps(d)#报错  TypeError:ObjectoftypedateisnotJSONserializable方案1:手动转换str()方案2:继承类......
  • 批量更新Postgresql的序列
    序列(sequence)是PostgreSQL中的一种对象,用于生成自动递增的唯一标识符。通常,序列会与表的自增主键一起使用,以确保每个新插入的行都有一个唯一的标识符。在某些情况下,可能需要更新序列的值:从另一个数据库中导入数据,自增列的值也从原来的数据中导入。导入的过程中,目标数据库的序列......
  • 时间序列预测(零)--简介
    时间序列预测可以称得上是一个及其普遍的一个算法问题,解决的方法也比较成熟,你可能第一时间想到的就是AR模型,以及各种自回归模型。然后xgboost似乎也能做时序问题,只是将原有的问题当成回归问题即可,某种意义上可解释性也能够得到一定的满足。再然后就是GRU、LSTM这类循环神经网络,借......
  • Java性能优化之序列化优化
    1、Java序列化及其缺陷Java提供了一种序列化机制,这种机制能够将一个对象序列化为二进制形式(字节数组),用于写入磁盘或输出到网络,同时也能从网络或磁盘中读取字节数组,反序列化成对象,在程序中使用。 JDK提供的两个输入、输出流对象ObjectInputStream和ObjectOutputStream,它......