GESP202512 八级 · 真题
难度 普及+/提高

P14924 宝石项链

环形项链切成最多的连续段,每段都含全部 m 种宝石:双指针求最短段 + 倍增统计。

一、先回答问题

一句话

从任意位置切成若干连续段,每段都要凑齐全部 m 种宝石,求最多能切多少段。

三步:①破环成链(数组复制成 2n);②双指针nxt[i]=从 i 开始最短能凑齐 m 种后的下一个起点;③倍增统计:枚举起点 i,沿 nxt 尽量多跳,落点不超过 i+n 的段数最大值即答案。

课堂安排:先理解“每段尽量短才能切更多”,再讲 nxt 的双指针,最后用倍增把“一段段跳”从 O(n) 加速到 O(log n)。

二、核心观察

两个关键点
贪心:每段取最短。段越短,给后面留的空间越大,总段数才可能最多。nxt[i] 就是“从 i 出发的最短合法段”的下一个起点。
最后一段可以变长。从 i 跳 k 次到 curr,只要 curr ≤ i+n,把剩下的零头并进最后一段(它仍含全部 m 种),就是一个合法的 k 段划分。所以条件是 nxt^k(i) ≤ i+n,用倍增求最大 k。

因为整条项链本身就含全部 m 种,所以每个起点至少能切出 1 段,答案 ≥ 1。

三、互动演示(样例 1:1 2 1 2 1 2,m=2)

破环成链后位置 1..12(后 6 个是复制)。选一个起点,沿 nxt 跳段,落点不超过 起点+6。

起点 i =

四、自己选答案

Q1. 为什么每段都取“最短的合法段”最优?

Q2. 处理“环”最常用的技巧是?

Q3. nxt[i] 可能等于 2n+1。若忘了给 f[2n+1][0] 设哨兵会怎样?

五、手推结果(起点 i=1)

跳第几段当前起点 currnxt[curr]≤ i+n=7 ?累计段数
1131
2352
357是 (=7)3
79否 (>7)3
起点 1 能切 3 段;其它起点同理也是 3,取最大 = 3

六、C++14 参考代码

致命坑:nxt[i] 可能 = 2n+1(一段恰好用到最后一颗)。必须 f[2n+1][0]=f[2n+2][0]=2n+2,否则倍增跳进 0 号下标、段数爆炸(样例 1 会输出 262143 而不是 3)。
读代码三处重点:①双指针 r 不回退,整体 O(n);②倍增表 f[i][k] = 从 i 连跳 2^k 段后的起点;③查询时从高位到低位试跳,落点 ≤ i+n 才接受。

复杂度:双指针 O(n),倍增 O(n log n);空间 O(n log n)。迁移:P2680 运输计划(倍增+树上差分)、P1083 借教室(区间差分)。