模拟赛的题,但是没做,所以现在来补。
题目简介
题目名称:
题目来源:
评测链接:https://www.luogu.com.cn/problem/P8277
形式化题意:存在一个长度为 U
和 D
组成的字符串
数据范围:
首先提一嘴部分分,是一个
最长上升子序列的优化
有一个众所周知的
但这样做显然是不优美的,所以考虑优化。
贪心与二分结合
考虑当前 $i$ 如果有多个选择,我们肯定选择 $a_i$ 最小的那一个,毕竟这样的话后面能选的概率就更大。
我们将当前构造出的 $\mathrm{LIS}$ 记为 $dp()$,其中 $dp(i)$ 就表示当前 $\mathrm{LIS}$ 的第 $i$ 位。对于我们现在需要加入的第 $x$ 位,我们找到 $dp()$ 中第一个比 $a_x$ 大的数,并将其替换为 $a_x$,而如果没有的话,就将 $a_x$ 添加到 $dp()$ 的末尾,最终答案就是 $dp()$ 的长度,而 $dp()$ 本身也是构造的一组解。
参考实现
1 | int len=1; |
这个是常数最小的做法。
树状数组优化dp
考虑 $\mathrm{LIS}$ 的本质就是一串逐渐变大的数字,所以我们考虑将 $a_i$ 从小到大依次填入。记录 $id(x)$ 表示 $a_x$ 在原序列里的位置。
每当我们要插入数 $a_i$ 的时候,查询 $[1,id(i))$ 之间已经维护到的最大值,也就是当前我们已知的最大 $\mathrm{LIS}$ 长度,然后我们在 $id(i)$ 处将其 $+1$ 就可以得到插入了 $a_i$ 之后的答案,最后的答案就是 $[1,n]$ 的最大值。
这里我们需要一个能够高效维护单点修改以及前缀查询最大值的数据结构,树状数组当然是首选,线段树和分块肯定也可以,但是效率提不起来。
参考实现
1 | struct Node |
当然,如果不记录 $id()$ 也是可以的,此时的 $\mathrm{BIT}$ 维护值域。
参考实现
1 | for(int i=1;i<=n;++i) Tr.add(a[i],Tr.query(a[i]-1)+1),maxa=std::max(maxa,a[i]); |
本质在于 $\mathrm{LIS}$ 的求解可以视作一个二维偏序问题。
考虑用 $f_i$ 表示在 $a$ 序列中以 $i$ 结尾能够最大匹配的 $b$ 序列的长度。
考虑 $f_i$ 的转移:
然后发现
记录
那么
然后问题在于
AC Code
1 | // ----- Eternally question----- |