日记
今天不用考试,所以是个补专题的好日子。
我们先来看看一组我搓的样例:
这是初始状态,红色的格子表示下一步能选什么。
这是选了一个的状态
这是选了两个的状态
我们可以发现,在题目的限制下,黑色的轮廓线总是向上或者向右,如初始状态下就是 上上上上右右右右
,第二种就是 上上上右上右右右
。这能发现什么,这不就能状压了吗?
我们设 dpsta 表示状态为 sta 时的答案。其中:sta 在二进制下的第 i 位为 1
表示这个轮廓线为上。那么初始状态为 ((1 << n) - 1) << m
。那么如何转移呢?先暂且设下一个状态为 nxt ,当前坐标为 (x,y) ,那么有:
dpnxt={dpsta+ax,ydpsta−bx,y该菲菲取该牛牛取然后重要的就是怎么求 nxt 。我们看:这个点能取,也就是说他左上的轮廓线是 上右
。到状态里就是 ...01...
。那怎么判断呢?很简单,只需要按位与 0b11
。我们枚举到第 i 位,当 (sta >> i) & 0b11 == 1
时,就说明满足条件了,我们就可以进行转移,nxt 就是 sta ^ (0b11 << i)
,就是将那两位取反,变成 右上
。而结束状态就是 (1 << m) - 1
。但是我们怎么判断当前是该该菲菲取还是该牛牛取呢?一种是写判断小函数,但是我太懒也太蒻了,写不来。于是我写的记搜,把 该谁取 放在形参里传下去就可以了。
核心代码:
solution
我们先想朴素的状态和转移:设 dpi,j 表示以 ai 结尾的长度为 j 的子序列个数。那么有 dpi,j=k∈[1,i),ak<ai∑dpk−1,j−1 。但是我们发现:枚举状态就有 O(nm) 的,而转移又有 O(n) ,合起来就是 O(n2m) ,这明显过不了啊。
我们考虑优化:我们发现 dpi,j 都是由 dpx,j−1 转移而来的,所以最外层循环可以是 j 。然后我们可以想到前缀和:以 ai 为下标,记录前 i−1 个数中 ak<ai 的 dpk,j−1 的前缀和,这个可以用树状数组。但是 ai 很大,要离散化。那么这样,我们成功的把dp优化到了 O(m×nlogn) 。
核心代码:
solution
我们先来想朴素转移:设 dpi,j 表示第 i 个工人刷到第 j 块木板的最大收益。那么有三种情况:
- 不需要第 i 个工人刷,答案为 dpi−1,j
- 第 i 个工人不刷第 j 块木板,答案为 dpi,j−1
- 第 i 个工人刷第 j 块木板,那么我们就枚举上个人所刷的区间的右端点 k ,但是保证 k+1∈[si−li,j] ,也就是下一块木板能被刷到。答案即为 dpi−1,k+pi×(j−k)
但是,我们发现这样的时间复杂度根本不可以,枚举状态是 O(nk) 的,转移还有一个 O(n) ,合起来就是 O(n2k) 。这不行啊。
我们考虑优化。我们发现复杂度的瓶颈主要是第三种情况的转移,注意到第三种情况可以变成这个样子:dpi−1,k−pi×k+pi×j 。但是,对于每一个 i,j 来说,pi×j 是不变的,那么我们就只用考虑最大的 dpi−1,k−pi×k 。我们发现这个东西可以用单调队列维护。
code: