好题一则

题意:给定 n,对所有 k[0,n],求满足:1的连续段的长度最大值恰好为 k 的01串个数。n106

场上思路:dpx,y: 长度为 x,最长的1连续段 len = y 的串个数。用了一些前缀和,从 O(n4) 优化到了 O(n3)

存在一些大力代数推导的容斥方法。但我不会(update:现在会了,在后面)。在题解区学到了一种很好的 DP 方法。记录一下。

首先是对dp状态设计的优化:考虑对每个 k 分别求答案。同时我们把恰好这个限制用前缀和改成**至多 **。

fn 表示长为 n 的串,末尾为1,gn 末尾为0。

fn=i=1kgni

gn=i=0n1fi

这里有一堆和式,考虑前缀和:

Fn=Fn1+Gn1Gnk1

Gn=Gn1+Fn1

联立一下,得到:

Gn=2Gn1Gnk2

边界:G0=1,Gn=0(n>0)

怎么快速算这个东西呢?

考虑这个东西的组合意义:你最初站在原点,权值为1,每次要么往后走1步,权值翻倍,要么跳k+2步,权值取反。问所有到 n 处的方案的权值之和。

那么我们直接枚举进行了几次跳就行了。其实这个东西和容斥的最后式子很像。得到:

Gn=i=0n/(k+2)(ni(k+2)+ii)2ni(k+2)(1)i

复杂度n乘调和级数。

upd:补了一下容斥做法:

还是把恰好这个限制用前缀和转化成至多。

枚举有几个0。我们不去固定每段长度(即乘上 (ni)),因为这样是不好分析每一段具体的限制的。我们采用划分段的思想:这些0把序列划分成了 i+1 个全 1 段。这样就可以很方便的处理有多少段不合法。

枚举不合法的段的个数 j,这些段至少有 k+1 个1。影响方案的只有「哪些段不合法」以及「剩下的1的分布」。容易得到:

i=0nj=0n/k(1)j(i+1j)(nij(k+1)+ii)

现在我们有了一个 O(n2logn) 的做法。

考虑优化这个式子。可以发现,j 的限制很严格,而 i 的限制较宽松,同时也很影响复杂度。但是这个很烦的 i 在两个组合数中分别各出现了一次。能不能让它少出现一次呢?

有个公式:(ab)(bc)=(ac)(acbc) 。组合意义:左边:从 a 个物品中先选 b 个,再从这 b 个中选 c 个;右边:先选出那 c 个,然后再选剩下的 b-c 个。

这个式子就把出现两次的 b 转化成了一个。而这个b的位置和 i 是一样的!

首先把 (i+1j) 拆开:(ij)(nij(k+1)+ii)+(ij1)(nij(k+1)+ii)

应用上面的式子,得到:

i=0nj=0n/k(1)j((nj(k+1)j)(nj(k+2)ij)+(nj(k+1)j1)(nj(k+2)+1ij+1))

然后再观察一下这个式子,考虑含 i 项的取值,可以发现,两个式子中含 i 项下标都是能把有值的区间遍历完的。即:i=0n(nj(k+2)ij)=2nj(k+2)。另一个同理。

那么我们就消去了一个求和号,剩下就很简单了。

总结一下两个方法:

  • 首先是最初的转化。可以发现两个方法都把恰好转成了至多。我考场上就没有想到这点。有的时候松的限制好做,有的时候紧的限制好做。需要具体分析。
  • 然后是一个方向的问题:O(nlogn) 的复杂度是可以接受的。第一个方法或许不明显,但那个递推式也算露出端倪。第二个方法的 n / k 这个上界几乎明示了这一点,因此向这个方向想是自然的。
  • 然后就是一些代数推导/组合意义。整个过程其实并不困难(甚至是很典),但这样一个题对于计数入门还说还是不错的。
posted @   rhineofts  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示