1 条题解
-
0
自动搬运
来自洛谷,原作者为

yummy
这个人是时代的眼泪,什么也没有留下搬运于
2025-08-24 23:05:04,当前版本为作者最后更新于2024-10-13 13:23:10,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
D. 配对序列 (pairing) 官方题解
本题考查的主要知识点有:
- 【4】简单一维动态规划
送分
读题后不难发现测试点 的输出为 。
对于测试点 ,一个数可以出现在答案中当且仅当该数字出现了 次。所以统计出现至少两次的数字种数再乘以 即可。
前 个测试点可以直接枚举子序列,然后判断是否符合题意,时间复杂度 。
如果考场上没有任何思路,那么这 分可以尝试拿到手。
朴素的 DP
题目也是让你求某种子序列的个数,类似求最长上升子序列,我们考虑 DP。
令 为以 结尾的最长配对子序列的长度,则每次转移要枚举 的上一个数的位置 和上上个数的位置 ,如果 且 ,那么 可以变成 。该方法时间复杂度为 ,可以通过测试点 拿到 分,如果结合送分,可得 分。
我们发现时间复杂度瓶颈在于每次转移需要枚举两个数。如果我们能把长度为奇数的子序列也写进某种 DP 里,那么每次转移只要枚举上一个数的位置。
令 表示以 结尾的最长配对子序列长度, 表示以 结尾,除了长度是奇数以外都满足配对子序列的要求的、最长子序列长度。
转移 时,枚举所有 ,如果 ,那么用 去更新 。
转移 时,枚举所有 ,如果 ,那么用 去更新 。
时间复杂度 ,可得 分,结合送分可得 分。
更优秀的 DP
经过观察,发现如果 ,且 ,那么 处的 DP 值一定大于 处的 DP 值(因为 的转移范围大于 的转移范围)。因此转移 时,我们只需要找到 之前最近的和 相等的 进行转移。实现上,在 从前往后扫描的过程中,用 来记录 中上一次出现数 的位置即可。
对于 ,如果是测试点 ,那么只需要在 从前往后循环的同时记录下 的最大值即可——毕竟因为所有数最多出现 次,如果 是第一次出现,那么 之前的 都有 ;而如果 是第二次出现,这个 就不可能再被某种 调用,正确性也就不重要了。
然而对于所有测试点,只记录 最大值(记为 )是不够的——如果 ,那么这时就要转而求“次大值”。总而言之,我们需要在 从前往后循环的同时,记录 不同的 前二大。
这样,总时间复杂度就是 的了,可以通过。
#include<bits/stdc++.h> using namespace std; const int N=500005; int n,a[N],even[N],odd[N],pre[N]; int main(){ scanf("%d",&n); int b1=0,b2=0; odd[0]=-1e9; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(a[b1]==a[i]) odd[i]=even[b2]+1; else odd[i]=even[b1]+1; even[i]=odd[pre[a[i]]]+1; if(even[i]>=even[b1]){ if(a[i]!=a[b1]) b2=b1; b1=i; } else if(even[i]>=even[b2] && a[i]!=a[b1]) b2=i; pre[a[i]]=i; } printf("%d\n",even[b1]); return 0; }
- 1
信息
- ID
- 10821
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者