1 条题解

  • 0
    @ 2025-12-8 21:32:35

    「自指01序列」题解

    问题分析

    我们需要计算长度为 nn 的 0-1 序列 ss 的数量,满足:

    • 对于每个长度为 kk 的连续子段(共 nk+1n-k+1 个)
    • 将该子段看作 kk 位二进制数(左侧高位,右侧低位),设为 tt
    • 要求 st=1s_t = 1(即序列的第 tt 位必须是 1)

    换句话说,序列 ss 必须满足:

    $$\forall i \in [0, n-k], \quad s_{\text{bin}(s_i s_{i+1} \cdots s_{i+k-1})} = 1 $$

    其中 bin(x)\text{bin}(x) 表示将二进制串 xx 转换为十进制数。

    关键约束

    设窗口 wi=sisi+1si+k1w_i = s_i s_{i+1} \cdots s_{i+k-1} 对应的数值为 tit_i,则要求 sti=1s_{t_i} = 1

    这意味着:每个长度为 kk 的窗口对应的数值,必须是一个 1 的位置索引

    重新理解问题

    我们可以将序列 ss 看作一个有向图上的路径:

    1. 状态表示

    考虑一个滑动窗口,当前窗口内容是 kk 位二进制数。当我们向右移动一位时,窗口内容从 xx 变为:

    • 去掉最高位,左移一位
    • 加上新的最低位 b{0,1}b \in \{0,1\}

    即:(x1)&mask+b(x \ll 1) \& \text{mask} + b,其中 mask=2k1\text{mask} = 2^k - 1

    因此,我们可以将每个 kk 位二进制数看作一个状态,共 2k2^k 个状态。

    2. 转移条件

    设当前窗口对应的数值(状态)为 tt,那么根据条件,sts_t 必须是 1。

    sts_t 是什么?tt 是当前窗口的数值,而 sts_t 是序列第 tt 位的值。这个值会影响未来的某个窗口吗?

    实际上,tt 是窗口数值,而 sts_t 是序列中位置 tt 的值。这个值会在我们滑动到某个位置时被检查,但不是立即检查。

    更准确地说:当窗口滑动到位置 ii 时,我们检查 stis_{t_i} 是否为 1,其中 tit_i 是窗口在位置 ii 时的数值。

    stis_{t_i} 是序列的固定位置,而窗口在不断滑动。这建立了一个自指约束:窗口数值指向序列的某个位置,该位置的值必须是 1。

    图论建模

    状态机模型

    定义状态为当前窗口的 kk 位二进制值,共 2k2^k 个状态,记为 0,1,,2k10, 1, \dots, 2^k-1

    序列 ss 对应一条长度为 nn 的状态路径:

    • 初始窗口:前 kks0sk1s_0 \dots s_{k-1} 对应状态 S0S_0
    • ii 步:添加 si+k1s_{i+k-1}(如果是前 kk 步,则是构建初始窗口;之后是滑动)

    约束:对于路径上的每个状态 tt(即某个窗口的数值),要求 st=1s_t = 1

    sts_t 不是当前状态,而是序列第 tt 位的值。这个值会在路径的某个位置出现:当窗口滑动到某个位置 jj 时,如果窗口数值等于 tt,那么我们要求 st=1s_t = 1

    sts_t 是序列的第 tt 位,它会影响以 tt 开头的窗口吗?不一定。

    关键观察

    vv 是一个 kk 位二进制数(状态)。如果 vv 作为窗口数值出现在路径中(即某个时刻窗口内容等于 vv),那么根据条件,svs_v 必须是 1。

    svs_v 是序列的第 vv 位,而 vv 是状态值。svs_v 会影响哪些窗口呢?

    考虑位置 vv:以 vv 开头的窗口内容是 svsv+1sv+k1s_v s_{v+1} \dots s_{v+k-1},这个窗口的数值是某个 ww。条件要求 sw=1s_w = 1

    所以这形成了一个链式约束。

    建立约束图

    定义一个有向图 GG

    • 节点:2k2^k 个状态(kk 位二进制数)
    • 边:如果状态 uu 可以通过添加一个比特 bb 转移到状态 vv,则存在边 uvu \to v,标记为 bb

    具体转移:v=((u1)&mask)bv = ((u \ll 1) \& \text{mask}) | b

    序列 ss 对应图上一条长度为 nn 的路径(从某个初始状态开始,经过 n1n-1 次转移)。

    约束:对于路径上的每个节点 uu(即某个窗口数值),要求 su=1s_u = 1

    sus_u 是什么?它是序列的第 uu 位的值,这个值由路径上的某条边决定。

    更准确地说:路径的第 uu 步(从0开始计数)输出一个比特 sus_u,这个比特是转移的标签。

    所以约束变为:对于路径上的每个节点 uu,要求在第 uu 步输出的比特是 1

    重新表述

    我们有一条长度为 nn 的路径,路径上的每个位置 ii 有一个状态 stateistate_i(窗口内容)和一个输出比特 sis_i

    约束:对于每个位置 iisstatei=1s_{state_i} = 1

    即:位置 ii 的状态值 stateistate_i 指向另一个位置 j=stateij = state_i,要求位置 jj 的输出比特为 1。

    这是一个自指约束:每个位置的状态指向另一个位置,要求该位置的输出为 1。

    动态规划算法

    状态定义

    由于 k4k \leq 42k162^k \leq 16,状态数很少。

    我们可以在 DP 中记录当前路径的最后 kk 个比特(即当前状态),以及已经确定的输出比特中哪些位置必须是 1。

    但约束涉及未来位置:当前状态 uu 要求位置 uu 的输出为 1。如果 uu 已经过去,我们检查是否满足;如果 uu 在未来,我们记录这个要求。

    定义 DP 状态

    • 当前位置 pospos(0 到 nn
    • 当前窗口状态 ststkk 位二进制数)
    • 一个集合 maskmask,表示哪些未来位置必须输出 1

    maskmask 可能很大(nn 可达 500),不能直接记录。

    关键简化

    由于 kk 很小,状态值 uu 的范围是 [0,2k1][0, 2^k-1],即 u<16u < 16

    因此,约束只涉及位置 0 到 15(如果 k=4k=4)或更少。

    所以,只有前 2k2^k 个位置的约束是关键的。对于位置 i2ki \geq 2^k,没有状态值会指向它(因为状态值最大为 2k12^k-1)。

    重要结论:只有前 2k2^k 个位置 s0,s1,,s2k1s_0, s_1, \dots, s_{2^k-1} 受到直接约束。其他位置 sis_ii2ki \geq 2^k)没有状态指向它们,所以它们可以是 0 或 1 自由选择。

    但等等:状态值 uu 指向位置 uu,而 u<2ku < 2^k,所以确实只涉及前 2k2^k 个位置。

    因此,约束简化为:对于每个位置 ii,如果 statei=jstate_i = j,则 sj=1s_j = 1

    其中 j<2kj < 2^k

    新的理解

    序列 ss 的前 2k2^k 个比特是受约束的,后面的比特自由。

    我们可以先确定前 2k2^k 个比特,然后检查是否满足所有约束,最后乘以 2n2k2^{n-2^k}(后面每个位置自由选择 0 或 1)。

    但约束是动态的:stateistate_i 依赖于序列的值。

    建立自动机

    由于 kk 很小,我们可以构建一个自动机,状态是当前窗口内容(kk 位),并且记录哪些位置已经满足了约束。

    定义状态为 (st,mask)(st, mask)

    • stst:当前窗口内容(kk 位)
    • maskmask:一个 2k2^k 位的位掩码,表示哪些位置已经确认为 1

    初始状态:(s0sk1,0)(s_0 \dots s_{k-1}, 0)

    转移:添加一个比特 bb

    • 新状态 st=((st1)&(2k1))bst' = ((st \ll 1) \& (2^k-1)) | b
    • 新掩码 mask=maskmask' = mask
    • 但我们需要添加约束:当前状态 stst 要求位置 stst 必须是 1。如果 stst 对应的比特在 maskmask 中尚未设置,我们需要设置它,但前提是 stst 在当前路径中出现了。

    实际上,约束是:如果状态 stst 出现在路径中,那么 sst=1s_{st} = 1

    在自动机中,当我们处于状态 stst 时,我们要求 sst=1s_{st} = 1。但 ssts_{st} 是什么时候输出的?它是路径的第 stst 步的输出。

    所以我们需要在自动机中记录:哪些位置已经输出了 1。

    更精确的自动机

    考虑构建序列的过程:我们逐步输出 s0,s1,,sn1s_0, s_1, \dots, s_{n-1}

    在输出 sis_i 时,当前窗口状态是基于最近的 kk 个输出。

    我们需要确保:对于每个 ii,如果存在某个 jj 使得 statej=istate_j = i,那么 si=1s_i = 1

    等价地:对于每个 iisis_i 必须为 1,除非没有状态等于 ii

    因此,问题转化为:有多少个序列 ss,使得集合 {i:si=0}\{i: s_i = 0\} 与所有状态值集合不相交?

    最终算法

    步骤1:枚举所有可能的状态值集合

    由于 k4k \leq 4,状态值范围 [0,2k1][0, 2^k-1] 最多 16 个值。

    对于序列 ss,设 A={i:si=1}A = \{i: s_i = 1\} 是 1 的位置集合。

    约束要求:所有出现在路径中的状态值都必须属于 AA

    但哪些状态值会出现在路径中?这取决于序列 ss 本身,因为状态由连续的 kk 个比特决定。

    步骤2:固定前 2k2^k 个比特

    由于状态值小于 2k2^k,只有前 2k2^k 个位置可能被要求为 1。

    我们可以枚举前 2k2^k 个比特的所有可能(22k2^{2^k} 种,最多 216=655362^{16}=65536),对于每种枚举:

    1. 检查是否自洽:对于每个位置 i<2ki < 2^k,如果 si=0s_i = 0,那么是否可能没有状态等于 ii
    2. 计算满足条件的序列总数(乘以 2n2k2^{n-2^k}

    但检查自洽性需要知道哪些状态会出现,这又依赖于序列。

    步骤3:建立约束系统

    定义变量 xi=six_i = s_i(0 或 1),i=0,,n1i=0,\dots,n-1

    约束:对于每个窗口起始位置 i=0,,nki=0,\dots,n-k: 设 ti=bin(xixi+1xi+k1)t_i = \text{bin}(x_i x_{i+1} \dots x_{i+k-1}) 要求 xti=1x_{t_i} = 1

    这是一个自指约束系统。

    我们可以尝试直接求解这个约束系统。

    步骤4:注意到 kk 很小,使用 DP

    由于 k4k \leq 4,我们可以使用状态压缩 DP。

    定义 dp[pos][mask]dp[pos][mask] 表示:

    • 已经确定了前 pospos 个比特
    • 当前窗口的最后 kk 个比特(即状态)为某个值,但我们需要记录更多

    更好的定义: dp[pos][st][req]dp[pos][st][req]

    • pospos:已经处理的位置数
    • stst:当前窗口状态(最近 kk 个比特)
    • reqreq:一个 2k2^k 位的掩码,表示哪些位置已经被确认为必须为 1

    转移:添加下一个比特 b{0,1}b \in \{0,1\}

    1. 新状态 st=((st1)&(2k1))bst' = ((st \ll 1) \& (2^k-1)) | b
    2. 新要求:当前状态 stst 要求位置 stst 为 1。所以如果 st<posst < pos,我们已经知道 xstx_{st} 的值,检查是否满足;如果 stposst \geq pos,记录这个要求。
    3. reqreq 掩码记录的是未来哪些位置必须为 1。

    具体地:

    • 当处于状态 stst 时,如果 st<posst < pos,检查 xstx_{st} 是否为 1
    • 如果 stposst \geq pos,则要求 xst=1x_{st} = 1,设置 reqreq 中的对应位

    最终,在 pos=npos=n 时,检查所有要求是否满足(reqreq 中要求为 1 的位置,对应的 xx 值是否为 1)。

    步骤5:简化 DP

    由于 kk 很小,我们可以将 stst 和部分历史信息编码。

    实际上,我们只需要知道最近 kk 个比特,因为窗口只依赖这些。

    DP 状态:(pos,st,req)(pos, st, req)

    • pospos:0 到 nn
    • ststkk 位状态,0st<2k0 \leq st < 2^k
    • reqreq2k2^k 位掩码,表示哪些位置必须为 1

    转移:对于 b=0,1b=0,1

    1. st=((st1)&(2k1))bst' = ((st \ll 1) \& (2^k-1)) | b
    2. 检查约束:状态 stst 要求 xst=1x_{st} = 1
      • 如果 st<posst < pos,已经确定了 xstx_{st},检查是否满足
      • 如果 stposst \geq pos,在 reqreq 中标记位置 stst 必须为 1
    3. xstx_{st} 的值是什么?对于 st<posst < pos,我们需要知道之前选择的 xstx_{st}。这没有记录在状态中!

    因此,我们需要记录历史选择的比特。

    步骤6:记录必要历史

    由于 st<2kst < 2^k,我们只需要知道前 2k2^k 个比特的值。

    所以我们可以将前 2k2^k 个比特的选择作为状态的一部分。

    定义状态:(pos,st,prefix)(pos, st, prefix),其中 prefixprefix 是前 2k2^k 个比特的选择(2k2^k 位掩码)。

    pospos 可能大于 2k2^k,此时 prefixprefix 已经固定。

    对于 pos2kpos \geq 2^kprefixprefix 不再变化。

    转移:

    • 如果 pos<2kpos < 2^k:选择 xpos=bx_{pos} = b,更新 prefixprefix
    • 检查约束:状态 stst 要求 xst=1x_{st} = 1,从 prefixprefix 中检查(如果 st<posst < pos)或记录要求

    但记录要求需要另一个掩码。

    最终可行算法

    由于 k4k \leq 42k162^k \leq 16,我们可以暴力枚举前 2k2^k 个比特的所有可能(最多 65536 种)。

    对于每种前 2k2^k 个比特的赋值,检查是否满足所有约束:

    • 对于每个窗口位置 i=0,,2kki=0,\dots,2^k-k(因为 n2kn \geq 2^k,且只需要检查到 2kk2^k-k 就能覆盖所有可能指向前 2k2^k 个位置的约束)
    • 计算 ti=bin(xixi+k1)t_i = \text{bin}(x_i \dots x_{i+k-1})
    • 检查 xtix_{t_i} 是否为 1

    如果满足,那么这种赋值是可行的。对于后面的 n2kn-2^k 个比特,可以自由选择 0 或 1,贡献 2n2k2^{n-2^k}

    因此,答案 = (满足条件的前 2k2^k 比特赋值数) × 2n2k2^{n-2^k}

    但需要验证:对于 i>2kki > 2^k-k 的窗口,tit_i 可能 2k\geq 2^k,此时约束 xti=1x_{t_i}=1 自动满足(因为 ti2kt_i \geq 2^k 的位置可以自由选择为 1)。

    实际上,对于 i2kk+1i \geq 2^k-k+1,窗口包含位置 2k\geq 2^k,这些位置的值是自由的。但 tit_i 可能仍然 <2k< 2^k

    例如,k=2k=2,序列长度 n=4n=42k=42^k=4。窗口位置 i=2i=2x2x3x_2 x_3,如果 x2=0,x3=1x_2=0, x_3=1,则 t=1t=1,要求 x1=1x_1=1。这仍然涉及前 4 个位置。

    所以我们需要检查所有窗口,直到 i=nki = n-k

    因此,算法为:

    1. 枚举前 2k2^k 个比特的所有赋值(22k2^{2^k} 种)
    2. 对于每种赋值,检查所有窗口 i=0,,min(2kk,nk)i=0,\dots,\min(2^k-k, n-k) 的约束
      • 实际上需要检查 i=0,,nki=0,\dots,n-k,但后面的窗口可能涉及自由位
    3. 如果所有约束满足,贡献 2max(0,n2k)2^{\max(0, n-2^k)}

    复杂度:O(22kn)O(2^{2^k} \cdot n),对于 k=4k=4216=655362^{16}=65536n=500n=50065536×500=3.3×10765536×500=3.3×10^7,可接受。

    验证样例

    n=4,k=2n=4, k=22k=42^k=4

    枚举前 4 个比特 x0x1x2x3x_0 x_1 x_2 x_3

    窗口:

    • i=0i=0x0x1=t0x_0 x_1 = t_0,要求 xt0=1x_{t_0}=1
    • i=1i=1x1x2=t1x_1 x_2 = t_1,要求 xt1=1x_{t_1}=1
    • i=2i=2x2x3=t2x_2 x_3 = t_2,要求 xt2=1x_{t_2}=1

    检查所有 24=162^4=16 种赋值:

    • 01110111t0=1t_0=1x1=1x_1=1 ✓;t1=3t_1=3x3=1x_3=1 ✓;t2=3t_2=3x3=1x_3=1
    • 11111111:同样满足

    其他都不满足。所以 2 种。

    后面 n2k=0n-2^k=0 个自由位,贡献 20=12^0=1

    总方案数 2,与样例一致。

    总结

    最终算法:

    1. 枚举前 2k2^k 个比特的所有可能赋值
    2. 对于每种赋值,检查所有窗口约束
    3. 统计满足条件的赋值数 cntcnt
    4. 答案 = cnt×2max(0,n2k)mod998244353cnt \times 2^{\max(0, n-2^k)} \mod 998244353

    由于 k4k \leq 42k162^k \leq 16,枚举 216=655362^{16}=65536 种情况可行。

    • 1

    信息

    ID
    5914
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者