专题:枚举、模拟与构造 —— J 组 T1/T2 的全部

本讲定位这是 CSP-J 复赛出现频率最高的一讲。 cspj-series/cspj-round2-analysis-2019-2025.md 的逐题统计显示,2019–2025 七年 28 道 J 组复赛题里,主考点落在”枚举 / 模拟 / 构造”的有 6 道,单独第一basic-03 排序与贪心、ds-seriesdp-series 三线各 5 道并列第二)。T1、T2 两个位置几乎被它承包。

但”枚举”和”模拟”最容易被当成”不需要学的东西”——不就是照着题目写吗? 本讲要说明的恰恰是:这两件事有明确的、可以练的工程方法,而且做错的代价被严重低估。本讲实测的五种模拟错法里,最高的一种在大数据上 99.40% 的测试组会出错。


本讲实测

先把本讲最硬的几个数字放在前面。

⚠️ 关于编译选项,必须先说清楚。 本讲的代码块由 tools/check-cpp-blocks.pyg++ -std=c++11(不开 -O2 编译运行,下面各节”实测输出”里的耗时就是该环境的数字。而正式评测机通常开 -O2,同样的代码会快 3~8 倍。本讲凡是涉及”多大规模能过”的结论,两套数字都给,并标明是哪一套。

这不是形式主义:本讲实测中有一条结论在两种编译选项下方向相反(见第 2 条),如果只测一种就会写出错误的结论。

  1. 枚举对象选错,规模直接爆炸:分糖果模型(P7909)里,朴素枚举每一个 $k$,$R-L+1=10^9$ 时要 3.0203 秒(不开 O2;开 O2 是 0.8845 秒),$R-L+1=5\times10^9$ 时要 15.1132 / 4.4298 秒;换成”只枚举 $\min(R-L+1,\,n)$ 个”,两种编译选项下都恒为 0.000000 秒——因为区间够长时答案直接是 $n-1$,一次循环都不用跑。

  2. ⚠️ 剪掉对称性:实际加速比不等于枚举量之比,而且差的方向取决于开不开 O2

    枚举量之比 实测加速比(不开 -O2 实测加速比(-O2
    二重循环加 $i<j$ 2.00x 2.12x(偏高) 1.52x(偏低)
    三重循环加 $i<j<k$ 6.02x 9.82x(偏高) 5.20x(偏低)

    所以”少枚举到 1/6 就快 6 倍”两种情况下都是错的,而且错的方向还相反。开 -O2 时不足额,是因为 for (j=i+1; ...) 内层长度不定、分支预测与循环展开都吃亏;不开 -O2 时超额,是因为循环本身的固定开销占了大头,少跑的那部分连带省掉了这份开销。记住结论就一句:加速比要实测,不能从枚举量推。

  3. 子集枚举 s=(s-1)&t 把 $4^n$ 变成 $3^n$,可行规模整整多 4 位。 一秒墙:不开 O2 时 $4^n$ 在 $n\le14$、$3^n$ 在 $n\le18$(0.8724 s);开 O2 时是 $n\le15$ 与 $n\le19$(0.8431 s,$n=20$ 要 2.5145 s)。$n=15$ 时两种写法的比值:不开 O2 是 1.8691 s vs 0.0327 s(57 倍),开 O2 是 0.8595 s vs 0.0107 s(80 倍)。

  4. ⚠️ 子集枚举写成 s=(s-1)&s,指数级会悄悄退化成线性。 $n=14$ 时本该平均枚举 291.9 个子集,实际只枚举 8.0 个(是 $n/2+1$,线性)——症状是”跑得飞快但答案错”,而不是 TLE。错误率 $n=14$ 时 99.91%这条与编译选项无关,是纯逻辑错误。

  5. ⚠️ 模拟题两个”大数据反而测不出”的错法:「转向后立刻多走一步」错误率 $n\le4$ 14.99% → $n\le16$ 5.14% → 大数据 0.07%;「漏判越界上界」是 47.51% → 56.84% → 18.60%非单调。而「统计步数而非不同格子」是 58.73% → 91.79% → 99.40%,随规模单调上升。三档规模缺一档就会得出相反的结论。

  6. 打表找规律的可行规模:$n\le11$。 暴力枚举全排列打错排数表,$n=11$ 要 1.3841 秒(不开 O2;开 O2 是 0.2422 秒),而 $n=12$ 要 16.8872 / 3.0283 秒——考场上打到第 11 项就该停手,而 11 项足够看出规律(找到递推后算 $D(20)$ 只要 0.000000 秒)。


前置与适合对象

前置

适合对象:准备 CSP-J 复赛的同学(本讲是收益最高的一讲);CSP-S 选手可跳过 §3.4 的基础实现技巧,重点看 §2.3 子集枚举与 §4。

本讲不讲(只指路):