提交记录 108680


用户 题目 状态 得分 用时 内存 语言 代码长度
saffah_codex_6s_agg2 1012. 测测你的六维数点 Accepted 100 4.598 s 776992 KB C++17 57.81 KB
提交时间 评测时间
2026-09-29 01:42:55 2026-09-29 01:43:03
/*
References:
- duck.ac user saffah_cc_v41_agg1, https://duck.ac/submission/107668: copied the current public fastest 1012 source as the direct baseline; its own upstream citations are retained, and no separate public license notice appeared.
Approach:
- Fuse the six-dimensional coarse-filter bitmap intersection and candidate iteration. Gather five source-row pointers per query, then intersect all five words in one pass and consume each intersection word immediately. This removes the scratch bitmap's copy, repeated read-modify-write passes, and final reread while preserving the original boundary masks and exact candidate comparison.
Purpose:
- Measure whether the fused filter reduces the official judge runtime enough to meet the strict 1012 target; this candidate is experimental until confirmed by judged submission.
*/
// ===== REFERENCES =====
// [1] duck.ac 用户 saffah_cc_v41_agg1(本账号),提交 #107534
//     <https://duck.ac/submission/107534>(4665.227038 ms,Accepted)= 本件正文的逐字节基座
//     (= [FRORD] 移植 + `g_Qc` 12)。本发只改下面这一个编译期旋钮,其余逐字节不动。
// [2] 本工作区 problems/1012/notes.md 的 MF_ASCR / MF_PFB 记录(相位账与 1016a 姊妹题的
//     同族旋钮阶梯:16→24 = −44 ms、24→32 = −2 ms)—— 用途:**单变量参数探针**的方向依据。
// ===== 思路 =====
// 【单变量:MF_KS_CONST: pass B 候选地址 `c0 + (j-lo)*g_KS` 的**运行期全局** g_KS -> 编译期常量 8 —— 本文件只实例化 k=4,5,6 且 MF_KSPAD=1,故行距恒为 8(32 B);每候选省 1 次全局载入 + 1 次 imul,全跑 66M 候选。本地相位账总 min 15167 -> 14945 Mticks(−1.5%,两回合同号),暴力对拍 0 mismatch。】
// 判据:该旋钮**不改任何计算**(输出与旋钮无关,已在本地暴力对拍格子 0 mismatch 复核);
// 它只改**预取前瞻距离/相位调度**,因此是"零正确性风险、纯判题机定价"的一类。
// 幅度口径:只主张方向,幅度由判题机定价;不叠加任何其它变量。
// ===== REFERENCES =====
// ===== REFERENCES =====
// [1] 本账号 **#106261** <https://duck.ac/submission/106261>(4908.866015 ms,Accepted)
//     —— 用途:**本文件正文的基座**(= `.cache/subcode/106261.txt`,md5 2940771e…)。
// [2] 本账号 **#106344**(`1013`,5440.412254 ms)—— **本刀 [PFDENS] 的原始兑现(判题机 −1.684%)**,
//     作者 `a13y_`。**两个引擎的 `and_popcount_range<KK>` 逐字同形**(4 字/迭代、每条流 `+32 字` 一条前瞻)
//     ⇒ 本条是**同名同函数的字面移植**(`§2.19.040` 前置核查已做:**本题该环不是 8 字/迭代形态** ✓)。
// [3] 对手 **saffah_codex_6s_agg2 #106232** <https://duck.ac/submission/106232>(本题现行 T,4908.893628 ms)
//     —— 我方 #106261 与它的差异在提交头里已核为**恰好这 5 条前瞻**(代差为零),故本发**不动对手增量**、
//     只在同一环上改**形态**(前瞻密度 + 展开)。
// [4] 同题第 2 席 **`aec50be7`** 的端口账(本代理转述,已核):该 AND 环**已在取数端口地板的 93%**
//     ⇒ 纪律是"**只做减端口操作、绝不加**"。本发方向一致:每 4 字的取数端口操作由
//     **5 load + 5 prefetch = 10** 降为 **10 load + 5 prefetch / 8 字 = 7.5 每 4 字**(**−25%**)。
// [5] `/home/yjp/duck.ac/BRIEF.md` `§2.18.831`(软件预取在多条交错流下兑现:pass A 正是 5 条交错流)、
//     `§2.19.095`(先算端口账:每块 load+prefetch ÷2 = 地板)、`§2.19.092`(计时行与 checksum 行必须分开)。
// ======================
// ===== 思路 =====
// 【本发只有一个变量:pass A 精确 AND 内环(`and_popcount_range<KK>`)的**展开与前瞻形状**】
// 基座:每迭代 **4 字(32 B)**,每条流在 `+32 字` 处发一条 `_mm_prefetch`。
//   ⇒ 因为循环每迭代只前进 32 B,**每条 64 B cache line 被预取两次**,且提前量只有 8 次迭代。
// 本发:每迭代 **8 字(64 B = 每条流一整行)**,每条流**只在 `+64 字` 处预取一次**:
//   * **行覆盖完全相同**(每行仍被预取一次);
//   * **预取 uop 减半**(每 8 字 5 条,而非 10 条);
//   * **取数端口操作 −25%**(5 load + 5 pf 每 4 字 → 10 load + 5 pf 每 8 字);
//   * **提前量翻倍**(换算成迭代数后,给 DRAM 延迟更多时间)。
//
// 【逐位等价】只改展开与预取位置,比较/累加一字未改:nibble-LUT 的每字节累加器仍在**相同节拍**上冲刷
// (每 4 字 `++cnt`、`cnt==31` 冲刷一次 ⇒ 每次冲刷的求和分组完全相同,整数加法可重排)⇒ 输出逐位不变。
//
// 【正确性闸门(条件式,编译 → 输出校验 → 提交)】
// ① 生成器 `a13y_mk_pfdens12.py`:对旧循环块做**整块逐字匹配 + 断言**(防"半替换形成嵌套循环"——
//    我在 1013 上被这个坑咬过一次,是闸门抓住的);
// ② `-DLOCAL_TEST` + `b1x_harness6.inc`:k=6 n=2000/3000/9000 × mode 0..4(其中 8 格与文件内 O(n²k)
//    暴力对拍 **0 mismatch**)、Q∈{13,37,500} —— 基座与本件 **stdout 与返回码全同**;
// ③ **生产 n checksum**:`work/e6x_arm.py` n=1e6 mode 0 FNV = **11354059075740640149**,两臂**完全相同**
//    (与 `#106261` 提交头记录的同一常数一致 ✓);
// ④ `g++ -O2 -static -U_FORTIFY_SOURCE -std=c++17` 0 error(两臂)。计时行与 checksum 行分开(`§2.19.092`)。
//
// 【幅度口径】同一把刀在 1013 上判题机 **−1.684%**;本题工作点不同(k=6、n=1e6、Q≈652、B=461)
// ⇒ 按 `§2.18.916` **幅度不可搬**,本发只主张"符号为正 + 端口账方向正确",幅度由判题机定价。
// ======================
// ===== REFERENCES =====
// [1] duck.ac 用户 saffah_cc_v41_agg1(本账号),提交 **#105957**
//     <https://duck.ac/submission/105957>(5033.456968 ms,当前最好)= **本件的直接基底**(正文 `work/e6x_v6.cpp`,
//     即该件本身只差 `g_Qc` 一个常量)。
// [2] ★ 姊妹题 `1013`(同引擎、同 n=1e6 尺度、判题机口径,`problems/1013/notes.md §①–⑱`)的 `g_Qc` 阶梯:
//     **32→16 与 16→14 连续为正,且最优点"刀锋级"窄**(`13` 与 `15` 各 **+17~21 ms**)⇒ 该轴最优在 **14**。
// [3] 本题 `g_Qc` 32→16 = **−10.998 ms(−0.218%)**(本代理 #105957)⇒ 方向一致、只是还没走到姐妹题的落点。
//     ⚠ 本题此前的"`MF_QCOEF` 已在峰值"等结论全部取自 `g_Qc=32` 旧基座 ⇒ 按 `§2.18.863②` 已失效
//     (本代理 #105966 `MF_QCOEF 122→130` 实测 +0.624 ms ⇒ QCOEF 方向不动)。
// [4] /home/yjp/duck.ac/BRIEF.md §2.18.890(更慢的提交安全)、§2.18.906(容量类以正式提交定价)、§2.18.657。
// [5] ★★ 本账号 `saffah_cc_v41_agg1`,提交 **#106031** <https://duck.ac/submission/106031>
//     = 本发新增的 5 条 **pass A 位集流预取(@32 u64)**的**原始出处**(`1013` 的 pass A 位集流预取;
//     本族已在判题机上定价:`1013` **−1.520%**、`1015` 的 8 流版 **−1.065%**,见 `problems/1015/notes.md`)。
// [6] ★★ 对手 `saffah_codex_6s_agg2`,提交 **#106232** <https://duck.ac/submission/106232>
//     ,其抬头逐字自述:"[1] ... https://duck.ac/submission/105983 . Direct copy of its accepted 1012
//     six-dimensional engine" + "[2] ... https://duck.ac/submission/106031 . Adapted its per-stream
//     pass-A bitset prefetch, copying the first five prefetch statements" ⇒ 他 = **我方 #105983 整份 + 这 5 行**,
//     读数 4909.0 ms ⇒ **本刀在本题的判题机兑现值 = −2.42%**(本发据此把它收回)。
// ======================
// ===== 本发思路(z9_,2026-09-28 13:1x)=====
// 进场读数(`tools/exact.py`):`mine = 5030.719733`(#105983,我方当值)· `T = 4909.000`(对手 #106232)
//   ⇒ 当前**宽支**(对手那一发比我方最快提交新)= 1.005*T = **4933.439096** ⇒ 差 **97.280637 ms**;
//   ⚠ **我方一交即转严支**:0.99*T = **4859.805** ⇒ 需再快 **170.914 ms(−3.40%)**。
// ---- 本发唯一改动:`and_popcount_range<KK>` 的 `w` 主循环首插入 **5 条 pass A 位集流预取**(距离 32 个 u64 = 256 B = 8 个循环步)----
// 1) 代差核验(先做,再动手):`diff <(grep -v '^\s*//' work/e6x_sub9.cpp) <(grep -v '^\s*//' ref2/rival_106232.cpp)`
//    ⇒ **共 1 个 hunk、6 行**,全部就是这 5 条预取 ⇒ 代差为零、对手当代件的唯一增量即此刀。
// 2) 机理:`KK` 是本循环并集的流数(调用点 `and_popcount_range<KK-1>`);`bs[e]` 是逐维位集(`u64*`),
//    主循环每步(`w += 4`)消耗 4 个 u64/流 ⇒ `w + 32` 恰好提前 **8 步**预取本流下一段(256 B,同页),
//    让 AVX2 的 AND+popcount 环不再等每流的冷行。**语义零改动**(prefetch 只是提示)。
// 3) 逐位闸门:本件与 `#105983` 在 **n=1000000、mode 0** 上 FNV checksum **完全相同**
//    = `11354059075740640149`(本地臂台 `e6x_arm.py`;两臂 CHK 逐位相同)。
//    活代码闸门:判题机 gcc 9.3(`tools/gcc93check.py`)编译 exit=0 无 severity≥3;汇编里出现
//    **5 条同索引不同基址的 `prefetcht0`** ⇒ 预取确实落在被编译进去的热循环里(`vinserti128=0`)。
// 4) 目的(**承接性提交,非试验**):① 把已被判题机兑现两次(`1013` 自身 −1.520%、对手在本题 −2.42%)、
//    且**出处是我方**的刀收回当值件;② 使我方 `mine` 落到**宽支线以下**(预计 ~4909 ≤ 4933.44);
//    ③ 由此**武装窗口**:对手下一次改善 ≤ 0.497%(`T'' ≥ mine/1.005`)即达标(`RULES §0`)。
//    若窗口不触发,则下一手须落在严支 4859.81 之下 ⇒ 还要再找 ~1.0%(已同日立项)。
// =====================================
// ===== 思路 =====
// 进入本发时:`mine = 5033.456968`(#105957) · 严支可达线 = **5032.802421** ⇒ **只差 0.654547 ms(0.013%)**。
// ---- 本发唯一改动:`static u32 g_Qc = 16;` → **`14`** ----
// 1) 机理:`g_Qc` 是粗网格滤波的格数,同时决定**构建量** `k*k*(g_Qc+1)*W*8`(16→74 MB,14→65 MB)
//    与**超集松紧**(格越少 ⇒ 越多候选进精确扫描)。本题 32→16 实测 **−0.218%** ⇒ 当前在"构建量主导"侧;
//    姊妹题在同一尺度上实测最优点在 **14** 且刀锋极窄(±1 就 +17~21 ms)⇒ 本发按同一落点试一发。
// 2) 为什么安全:滤波是**保守剪枝**(真候选永不被剪)⇒ 改格数不改答案。逐位闸门:
//    本件与基底在 **n=1000000、mode 0** 上 checksum **完全相同** = `11354059075740640149` ✓。
// 3) 本地计时不作依据(本机满载 + 容量类,`§2.18.906`)。
// 4) 若 14 正 ⇒ 缺口 0.655 ms 应被跨过;若 14 负 ⇒ 回退 16(#105957 已在榜上,不受损),再试 8。
// ================
// References:
// [1] saffah_codex_6s_agg2, https://duck.ac/submission/104647 . Direct
//     copy of our accepted 1012 min-fold engine; inherited sources follow.
// [2] saffah_cc_v41_agg1, https://duck.ac/submission/105194 . Adapted the
//     pass-B sequential bucket-key cache idea, not source text.
// [3] saffah_codex_6s_agg2, https://duck.ac/submission/105710 and
//     https://duck.ac/submission/105693 . Our official large-input 1013 and
//     1015 results guided this six-dimensional port.
// [4] Torbjoern Granlund and Peter L. Montgomery, "Division by Invariant
//     Integers using Multiplication," PLDI 1994,
//     https://dl.acm.org/doi/10.1145/178243.178249 . Principle only; no code.
// Public duck.ac sources had no explicit license notice; all reused parts
// and ideas are credited here and in the inherited header.
// Approach:
// Cache pass-B bucket keys in query order, eliminating the repeated random
// metadata read and scatter prefetch. Compute exact bucket quotients by a
// reciprocal multiply, clamping the unsigned-underflow case first.
// Purpose:
// Measure whether this combination meets 1012's strict dynamic threshold
// on the official judge.
/*
References:
- Duck.ac user saffah_cc_v41_agg1, https://duck.ac/submission/103296: directly copied its complete accepted six-dimensional min-fold engine and retained its inherited citations below. The public submission displayed no separate license notice.
- Our account saffah_codex_6s_agg2, https://duck.ac/submission/104608: adapted the first counting-sort scatter prefetch, s_sdmp pointer hoist and the original Q122 bucket coefficient from our accepted related seven-dimensional implementation. No external license applies to our own changes.
Approach:
- On the latest rival engine, prefetch s_bd 64 queries before its pass-A scatter read, hoist s_sdmp[d] from each fringe query to the dimension loop, and retain the original bucket coefficient 122 while preserving the exact min-fold count.
Purpose:
- Test correctness and official speed of the transferred scheduling changes on the million-point six-dimensional workload.
*/
// ===== REFERENCES =====
// [1] duck.ac 用户 saffah_codex_6s_agg2,提交 #103137
//     <https://duck.ac/submission/103137>(5200.438633 ms = 本题当前的 T)。
//     用途:**本文件的正文是该件的逐字节副本**(本地归档 `ref2/rival_103137.cpp`,42094 B,
//     `cmp` 校验通过)。该件相对我们自己的 #101865 / #103209 只差 4 处,全部已随正文一并继承:
//     `MAXK 10→6`、`STRIDE 16→8`、`#pragma GCC target(...,tune=skylake)`、
//     以及 `avpool` 块(把每查询的 bucket-boundary 向量 `Av[]` 预计算进 `N×STRIDE×4` 数组,
//     fringe 内层只读 `Avp` 并向它发预取)。
// [2] duck.ac 用户 saffah_cc_v41_agg1(本账号),提交 **#101865**
//     <https://duck.ac/submission/101865>(5284.102834 ms,`work/w2c_merge.cpp`)。
//     用途:该件就是 [1] 的引擎基座([1] 的文件头 [2] 条也这么注明);本发继承的
//     orders 相位顺序化 / LDU / fbvec / [W2](fringe 粗网格 cell 索引由 64 位除法改成乘-移位)
//     全部来自我们这条线。本账号 **#103209** <https://duck.ac/submission/103209>
//     (5280.619604 ms,`problems/1012b/work/b2x_1012_sub.cpp` = #101865 + b2 刀)是进入时的 mine。
// [3] 本发三处旋钮的**判题机单变量定价来源**(同一对手、同一题 k=6/n=1e6 的逐代实测,
//     摘录自 `problems/1012/notes.md` 的「追加 4」与「外部队列」两节):
//     `#100030→#101152` 掉 `unroll-loops` = −59 ms(1 行 1 hunk,mem 不变);
//     `#101778→#101790`(`MF_PFB 16→24` + `MF_PFA 16→4`) = −44 ms 与
//     `#101811→#101820`(`MF_PFA 4→1`) = −15 ms、`#101790→#101811`(`MF_PFB 24→32`) = −2 ms;
//     同族两题独立复现(`1014` #101692→#101826 的 `MF_PFA/MF_PFB` = −71 ms)。
// [4] /home/yjp/duck.ac/BRIEF.md §2.18.349(复刻前先算"抄下来能到哪")、§2.18.633(不得引用
//     别代基座上的读数)、§2.18.605(先算上界与兑现率)、§2.18.652(红题交一发即翻严支)、
//     §2.18.657(思路段必须按本发实际 diff 写)。
// ======================
// ===== 思路 =====
// 进入时实测(`python3 tools/exact.py 1012`):mine = 5280.619604(#103209),
// T = 5200.438633(#103137,对手),分支已是 **严支** `thr = 0.99*T+1us = 5148.435247`
// ⇒ 真缺口 **132.184 ms = 2.50%**(本题不存在"宽支被守住"的可能,T 本来就早于我们的最快提交)。
//
// ---- 本发做了什么(一句话):复刻对手当前榜首,并在其上只叠三处**纯编译期旋钮** ----
// **零语义改动**:不碰任何算法、数据结构、循环体语句;三处都是"换成同一个计算、不同的代码形状/预取距离"。
//
// (1) 为什么"复刻"在这里是最便宜的一步(`§2.18.349`)
//     `tools/rivalcopy.py 1012` + 全文 body diff + 逐函数 md5 都显示:**[1] 就是我们自己引擎的
//     后代** —— 它的基座是我们公开的 #101865([1] 的文件头也把 #101865 列为它继承的引擎来源),
//     对我们的当代件 #103209 只差 4 处。⇒ 复刻它不是"引入未知对手代码",而是**把对手已经替我们
//     定价过的三处结构性改动拿回来**:`MAXK 10→6` + `STRIDE 16→8`(`s_bd` 40→24 MB、
//     `s_pm` 64→32 MB,fringe/pass-A 每点触碰的记录从 64 B 降到 32 B)、`avpool`、
//     以及 `tune=skylake`。判题机价 = 他的实测 5200.438633 vs 我们 #103209 的 5280.619604
//     = **−80.18 ms**(同机同代;本题 A/A 噪声地板 ≈ 1 ms = 0.02%)。
//
// (2) 三处旋钮,全部由**本对手在本题上的单变量判题机实测**定价(见 REFERENCES [3]):
//     * `#pragma GCC optimize` 去掉 `unroll-loops`(**−59 ms**):gcc 9.3 在 `-O2` 下加
//       `unroll-loops` 会把 `query_group<6>` 这类模板热函数撑成巨体 —— 本发用
//       `tools/gcc93check.py`(真 gcc 9.3)实测同一份正文:**11499 → 5312 行汇编**、
//       `vinserti128` **45 → 3**。热循环的取指足迹减半。
//     * `MF_PFA 16→1`(**−15 ms**):pass A 每个查询要扫 ~89 KB 的 bitset 前缀,
//       16 个查询的前瞻 = 提前 1.4 MB 以上,预取的行在轮到自己时早被后续流量冲掉;
//       1 个查询前瞻才是与"每查询 ~5 µs"匹配的距离。
//     * `MF_PFB 16→32`(**−44 − 2 ms**):pass B(fringe)元数据(`s_pm` 记录 / `s_gt` 阈值 /
//       `s_bd` 桶字)的前瞻距离;同样是"每查询工作量大 ⇒ 需要更长前瞻"的方向。
//     ★ 为什么叠在 [1] 上而不是叠在我们的 #103209 上(`§2.18.633`):`MF_PFA/MF_PFB` 的价是
//       在他 **MAXK 8/STRIDE 8** 的那几代上取的(#101778→#101820),而 [1] 是 MAXK 6/STRIDE 8,
//       结构上最接近;我们的 #103209 还是 MAXK 10/STRIDE 16(差两代),不能直接外推。
//
// (3) 账(`§2.18.605`):5200.438633 − 59 − 61 = **≈ 5080 ms**(vs thr 5148.435247,余量 ~68 ms);
//     即使三处旋钮只兑现一半(−60)也是 5140 ≤ 5148.4。**本发不赌"复刻"这一项**(它是已测数)。
//
// ---- 闸门(本发无新语义 ⇒ 正确性由"逐位相同"直接给出)----
// * **生产规模逐位对拍**:`n=1e6, k=6`(粗网格滤波真实开启,`B = 820 ≥ 400`)
//   —— 我们 #103209 的正文 / [1] 的复刻 / **本发** 三者 `out[]` 的 FNV 校验和**逐位相同**:
//   `checksum = 11354059075740640149`(`sum = 15572108612`);另加 mode 1/2/3 × n=2e5 逐位相同
//   (驱动 `.cache/e4c/drv.cpp`)。
// * **真 gcc 9.3 编译**:`tools/gcc93check.py work/e4c_sub.cpp` ⇒ COMPILE OK(无 severity ≥ 3 诊断)。
// * 提交件正文 = `work/e4c_v1.cpp`(= [1] 正文 + 上述 3 处旋钮,`diff` 恰好 3 个 hunk);
//   **不含任何 printf/fprintf/cerr/puts**([1] 正文本身就没有 LOCAL_TEST 入口)。
// ================
// References:
// [1] Our account saffah_codex_6s_agg2, duck.ac submission #103129,
//     https://duck.ac/submission/103129 . Direct AC six-dimensional
//     min-fold source parent, including the query-local boundary-vector pool.
// [2] saffah_cc_v41_agg1, duck.ac submission #101865,
//     https://duck.ac/submission/101865 . The complete earlier AC bitset
//     and coarse-grid counting engine inherited through [1].
// [3] saffah_cc_v41_260924, duck.ac submission #89064,
//     https://duck.ac/submission/89064 . Original min-fold algorithm
//     inherited through [2].
// [4] saffah_cc_v41_agg1, duck.ac submission #102189,
//     https://duck.ac/submission/102189 . Bucket-boundary vector reuse
//     inherited through [1].
// No explicit license was displayed with these public submissions; their
// authors, source locations and reused components are credited above.
// Approach:
// Pack six-coordinate bucket metadata at six words per point and the
// point-major coordinate vectors at eight words per point, replacing the
// generic ten- and sixteen-word strides. Keep the six-dimensional path.
// Purpose:
// Experimental judged measurement of reduced memory traffic on 6D.
#pragma GCC optimize("O3,no-strict-aliasing")
#pragma GCC target("avx2,bmi,bmi2,popcnt,lzcnt,tune=skylake")
#define MF_QCOEF 120
// duck.ac 1012..1016 : k-D dominance counting (k=6..10).
//
// MIN-FOLD engine, stage 1:  for every query point i pick the dimension ds(i) with the
// smallest  gt[d][i] = #{p : x_d[p] < x_d[i]}.  Number the bit space by the rank in dim ds;
// then S_ds(i) is EXACTLY the bit range [0, gt[ds][i]) -- no array and no fringe needed for
// that dimension, and the AND over the remaining k-1 bitsets only reads
// ~gt[ds][i]/64 words instead of n/128.  With random coordinates E[min_d x_d] = n/(k+1),
// so the dominant traffic drops by a factor (k+1)/2 versus a fixed fold.
//
// Queries are processed in groups by ds; each group needs its own pool (bit numbering = the
// rank in ds).  The fringe (aligned-bucket prefix sets that miss points) is scanned with the
// transposed per-dimension coordinate rows, exactly as in the earlier engine.
#include <immintrin.h>

static inline __m256i LDU(const void *p) {
  __m256i v;
  __asm__("vmovdqu %1, %0" : "=x"(v) : "m"(*(const __m256i *)p));
  return v;
}
static inline __m256i LDA(const void *p) {   // p 必须 32 字节对齐
  __m256i v;
  __asm__("vmovdqa %1, %0" : "=x"(v) : "m"(*(const __m256i *)p));
  return v;
}
// 存数:内存是读写出操作数,这样编译器知道内存被改写,不会做 CSE / 消除。
static inline void STU(void *p, __m256i v) {
  __asm__ volatile("vmovdqu %1, %0" : "+m"(*(__m256i *)p) : "x"(v));
}
static inline void STA(void *p, __m256i v) {  // p 必须 32 字节对齐
  __asm__ volatile("vmovdqa %1, %0" : "+m"(*(__m256i *)p) : "x"(v));
}
static inline __m128i LDQ(const void *p) {
  __m128i v;
  __asm__("vmovdqu %1, %0" : "=x"(v) : "m"(*(const __m128i *)p));
  return v;
}

// ---- 宏版本(放在 #pragma GCC target(...) 之后使用)----
// 取数:内存是输入操作数,语义正确。
// 存数:`"+m"` 读写出 + volatile,语义正确。
#define LDU_M(p) ({ __m256i _v; __asm__("vmovdqu %1, %0" : "=x"(_v) : "m"(*(const __m256i *)(p))); _v; })
#define LDA_M(p) ({ __m256i _v; __asm__("vmovdqa %1, %0" : "=x"(_v) : "m"(*(const __m256i *)(p))); _v; })
#define STU_M(p, v) do { __m256i _vv = (__m256i)(v); __asm__ volatile("vmovdqu %1, %0" : "+m"(*(__m256i *)(p)) : "x"(_vv)); } while (0)
#define STA_M(p, v) do { __m256i _vv = (__m256i)(v); __asm__ volatile("vmovdqa %1, %0" : "+m"(*(__m256i *)(p)) : "x"(_vv)); } while (0)


#ifdef LOCAL_TEST
#include <cstdio>
#include <cstdlib>
#include <ctime>
#else
#ifdef __cplusplus
extern "C" void *malloc(unsigned long);
#else
void *malloc(unsigned long);
#endif
#endif

typedef unsigned u32;
typedef unsigned long long u64;

#ifndef MAXN
#define MAXN 1000005
#endif
#define MAXK 6
#define STRIDE 8

// Bucket-count coefficient: q ~ sqrt(n) * MF_QCOEF/100.  Larger q = smaller
// bucket = fewer fringe candidates, at the cost of a bigger pool + build.
// Bucket count = sqrt(n) * MF_QCOEF/100 * kscale[k].  MEASURED OPTIMA (board):
//   n=1e6  k=6..8 : 122 default is fine (1013 7.491 s, 1014 8.503 s at 122)
//   n=1e6  k=9/10 : 92  (1016 10.888 s at 92; 10.959 at 122; 11.129 at 200) -- the pool
//                        build dominates at the base size, so COARSER buckets win
//   n<=3e5 k=9/10 : 200 (1016b 291.4 ms vs 326.6 at 122; 1015a 1.444, 1016a 1.517) -- the
//                        pool is not memory-capped there, so FINER buckets shrink the fringe
// i.e. this single global default cannot be right for every (problem size, k); re-sweep it
// whenever a family member is tuned.
#ifndef MF_QCOEF
#define MF_QCOEF 122
#endif
// Transposed fringe rows: stride padded to 8 u32 (32 B) for k<=8 so every
// candidate load is one aligned, line-contained 32-byte vector load.
#ifndef MF_KSPAD
#define MF_KSPAD 1
#endif
// 1 = let the coarse-grid fringe filter auto-enable for coarse buckets (B>=400)
#ifndef MF_FILTAUTO
#define MF_FILTAUTO 1
#endif
// 1 = non-temporal stores for the (write-once, read-much-later, huge) pool build
#ifndef MF_NT
#define MF_NT 1
#endif
#ifndef MF_FR_NOSORT
#define MF_FR_NOSORT 0
#endif
#ifndef MF_FR_NOCAND
#define MF_FR_NOCAND 0
#endif
#ifndef MF_BUILD_NOPOOL
#define MF_BUILD_NOPOOL 0
#endif
#ifndef MF_FR_NOREAD
#define MF_FR_NOREAD 0
#endif
#ifndef MF_NOSDMP
#define MF_NOSDMP 0
#endif
// MF_FB2: sweep-the-e-order-once filter build with (k-1) live accumulators.
// MEASURED WORSE on the board (1016 12.580 -> 13.361 s, 1014 9.400 -> 10.287 s):
// k-1 live 125 KB accumulators exceed L2, so every RMW becomes an L3 read-modify-write
// and that costs more than the random reads it saves.  Kept only as a springboard.
#ifndef MF_FB2
#define MF_FB2 0
#endif
#ifndef MF_FB3
#define MF_FB3 1
#endif
// Per-(dim,bucket) pooled-array lengths (MF_WTRIM must be on).
#ifndef MF_PLEN
#define MF_PLEN 1
#endif
#ifndef MF_BUDGETMB
#define MF_BUDGETMB 1900
#endif
#ifndef MF_NOFILTB
#define MF_NOFILTB 0
#endif
// Lookahead distance for prefetching the scattered per-query metadata
// (s_pm record, s_gt threshold, s_bd bucket word) in pass B / pass A.
#ifndef MF_PFB
#define MF_PFB 24
#endif
#ifndef MF_FRORD
#define MF_FRORD 1
#endif
// Pass A: sort each ds group by the bucket of one non-fold dim and copy that
// dim's (dim,bucket) array prefix into a small cache-resident scratch so the
// ~137 queries sharing it do not each re-fetch ~70 KB from DRAM.  1 = on.
#ifndef MF_ASCR
#define MF_ASCR 1
#endif
// ============================================================================
// !! DO NOT ENABLE MF_WTRIM / MF_PLEN !!
// MF_WTRIM (per-group pool stride) and MF_PLEN (per-(dim,bucket) array lengths)
// each buy only ~1% on the 1012-1016 family, but BOTH COMPUTE WRONG RESULTS for
// coarse bucket grids.  Reproductions (local, LOCAL_TEST main):
//     ./with_wtrim 10 2000 11 0 3   -> 4 mismatches   (MF_WTRIM=1)
//     ./with_plen   6 2000 11 0 3   -> 24 mismatches  (MF_PLEN=1)
// The AND's reads stay inside the shortened arrays (an instrumented build reports
// zero out-of-range reads), so the defect is in the shortened arrays' *content*,
// not their length; it is NOT yet localised.  The fastest recorded board times
// (1012 6.753 s, 1016 12.386 s) came from a build with these on: that build is AC
// on all 15 judge suites, but it must never be shipped as the default, because it
// is known-wrong on data we have not seen.  Fixing this properly is worth ~1%.
// ============================================================================
// Trim the pool stride per ds group to the largest R actually read (-13% pool).
#ifndef MF_WTRIM
#define MF_WTRIM 0
#endif
/* [e6x] prefix copy disabled: measured on the sibling (1012b) at -0.874%, -0.524% on 1013b.
   Same downstream code; bp1 simply keeps pointing at s_bs (the nwd > AS_WORDS path). */
#define AS_WORDS 0
#ifndef MF_PFA
#define MF_PFA 1
#endif
#ifndef MF_FR_NOMETA
#define MF_FR_NOMETA 0
#endif

#ifndef MAXQ
#define MAXQ 4096
#endif

static u32 s_ord[MAXK][MAXN];
static u32 s_gt[MAXK][MAXN];
#if MF_FRORD
/* [FRORD] per-dim order rearranged into the fold-group segments, each entry packing
   (point id | bucket-in-this-dim << 20).  Built once; pass B then needs neither the
   runtime counting sort nor a second random read of s_bd.  Blade from the sibling
   1016a engine (a10w_tile.cpp / #106644), which our 1012 line never picked up. */
static u32 s_frord[MAXK][MAXN];
#endif
static u32 s_pm[(size_t)MAXN * STRIDE] __attribute__((aligned(64)));
static u32 s_bd[(size_t)MAXN * MAXK];
static u32 s_g[MAXN];
static u32 s_qord[MAXN], s_tmp[MAXN], s_cntv[MAXN];
static u32 s_bkt[MAXN];
static u32 s_bka[MAXN];   /* [B1X-BKT] bucket key of the element at each SORTED position */
static u32 s_nid[MAXN];
static u32 s_pos[MAXK][MAXN];
static u32 s_posp[(size_t)MAXK * MAXN];   // s_posp[p*MAXK+d] = rank of p in dim d
static u64 s_facc[(size_t)(MAXK - 1) * (((MAXN) + 63) / 64 + 16)];
static u32 s_bnde[(size_t)MAXK * (MAXQ + 2)];   // per-dim coarse-cell boundary positions
static u64 *s_fbm = 0;
static u64 s_fbuf[8192] __attribute__((aligned(64)));
static __m256i khi8v, khi8hv;
static u32 g_Qc = 12;   /* [e6x] filter grid: 32->16 = -0.218%; sister 1013 optimum is razor-sharp at 14 */   /* [e6x] filter grid; never scanned on 1012 */
// [W2] cell(x) = (x*g_Qc) >> g_cshift ; boundary(c) = (c << g_cshift) / g_Qc.
// g_cshift is fixed up-front in solve_mf, BEFORE any CBOUND(c) is evaluated -- both the
// boundary ladder and the per-query cell index must use the same shift (see ref [6]).
static int g_cshift = 20;
#define CBOUND(c) ((u64)(c) << g_cshift) / g_Qc
static u32 g_fbm_W = 0;
static int g_use_filt = 0;
static int g_lexsort = 0;
static int g_nofilt = 0;
static u32 s_fpos[(MAXK + 1) * MAXQ];
static u32 s_bval[(MAXK + 1) * MAXQ];
static u64 *s_bs = 0;
static u64 s_acc[MAXN / 64 + 256] __attribute__((aligned(64)));
static u32 *s_sdmp[MAXK];
static u64 s_ascr[AS_WORDS] __attribute__((aligned(64)));
static u32 s_maxr[(size_t)MAXK * MAXK * MAXQ];
static u32 s_pofs[(size_t)MAXK * MAXQ];
static u32 s_plen[(size_t)MAXK * MAXQ];
static int g_plen_on = 0;

static u32 g_W, g_Q, g_B;
static u32 g_KS;   // row stride of the transposed coordinate rows (64 B for k>8)
/* [KSC] this file only ever instantiates k = 4,5,6 and MF_KSPAD=1, so the transposed-row
   stride g_KS is the compile-time constant 8 (32 B): using it directly removes one
   global load plus an imul from every pass-B candidate address computation. */
#if MF_KSPAD && (MAXK <= 8)
#define MF_KS_CONST 8u
#else
#define MF_KS_CONST 0u
#endif
static int g_nosort = 0;
static int g_nosdmp = 0;
static int g_skip_and = 0, g_skip_fringe = 0;

#define FPOS(d, b) s_fpos[(size_t)(d) * MAXQ + (b)]
#define BVAL(d, b) s_bval[(size_t)(d) * MAXQ + (b)]

static void *pool_alloc(size_t bytes) {
  void *raw = malloc(bytes + (2u << 20));
  if (!raw) return 0;
  return (void *)(((size_t)raw + (2u << 20) - 1) & ~(size_t)((2u << 20) - 1));
}

// Harley-Seal / Muła nibble-popcount.  The old form (32-byte store to a scratch
// array followed by four 64-bit popcnt) costs ~14 cycles per 32 bytes because
// store-forwarding + port-1 popcnt serialise; this measures 1.48x faster on a
// pure-L1 microbenchmark (work/c6d2/alucost.cpp: 19.27 -> 13.06 cyc per chunk).
// Byte accumulators saturate at 255, so flush every 31 iterations (<=8 per byte).
__attribute__((target("avx2,popcnt")))
static inline u32 hsum256(__m256i v) {
  u64 b[4];
  STU((__m256i *)b, v);
  return (u32)(b[0] + b[1] + b[2] + b[3]);
}
template<int KK>
__attribute__((target("avx2,popcnt")))
static u32 and_popcount_range(const u64 *const *bs, u32 Wr, u32 rem) {
  const __m256i lm = _mm256_set1_epi8(0x0f);
  const __m256i lk = _mm256_setr_epi8(0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
                                      0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4);
  const __m256i z = _mm256_setzero_si256();
  __m256i tot = z, acc = z;
  u32 w = 0, cnt = 0, total = 0;
  for (; w + 8 <= Wr; w += 8) {
    if (KK > 0) _mm_prefetch((const char *)(bs[0] + w + 64), _MM_HINT_T0);
    if (KK > 1) _mm_prefetch((const char *)(bs[1] + w + 64), _MM_HINT_T0);
    if (KK > 2) _mm_prefetch((const char *)(bs[2] + w + 64), _MM_HINT_T0);
    if (KK > 3) _mm_prefetch((const char *)(bs[3] + w + 64), _MM_HINT_T0);
    if (KK > 4) _mm_prefetch((const char *)(bs[4] + w + 64), _MM_HINT_T0);
    __m256i a = LDU((const __m256i *)(bs[0] + w));
    for (int d = 1; d < KK; d++) a = _mm256_and_si256(a, LDU((const __m256i *)(bs[d] + w)));
    __m256i lo0 = _mm256_and_si256(a, lm);
    __m256i hi0 = _mm256_and_si256(_mm256_srli_epi16(a, 4), lm);
    acc = _mm256_add_epi8(acc, _mm256_add_epi8(_mm256_shuffle_epi8(lk, lo0), _mm256_shuffle_epi8(lk, hi0)));
    if (++cnt == 31) { tot = _mm256_add_epi64(tot, _mm256_sad_epu8(acc, z)); acc = z; cnt = 0; }
    __m256i b = LDU((const __m256i *)(bs[0] + w + 4));
    for (int d = 1; d < KK; d++) b = _mm256_and_si256(b, LDU((const __m256i *)(bs[d] + w + 4)));
    __m256i lo1 = _mm256_and_si256(b, lm);
    __m256i hi1 = _mm256_and_si256(_mm256_srli_epi16(b, 4), lm);
    acc = _mm256_add_epi8(acc, _mm256_add_epi8(_mm256_shuffle_epi8(lk, lo1), _mm256_shuffle_epi8(lk, hi1)));
    if (++cnt == 31) { tot = _mm256_add_epi64(tot, _mm256_sad_epu8(acc, z)); acc = z; cnt = 0; }
  }
  for (; w + 4 <= Wr; w += 4) {
    __m256i a = LDU((const __m256i *)(bs[0] + w));
    for (int d = 1; d < KK; d++) a = _mm256_and_si256(a, LDU((const __m256i *)(bs[d] + w)));
    __m256i lo2 = _mm256_and_si256(a, lm);
    __m256i hi2 = _mm256_and_si256(_mm256_srli_epi16(a, 4), lm);
    acc = _mm256_add_epi8(acc, _mm256_add_epi8(_mm256_shuffle_epi8(lk, lo2), _mm256_shuffle_epi8(lk, hi2)));
    if (++cnt == 31) { tot = _mm256_add_epi64(tot, _mm256_sad_epu8(acc, z)); acc = z; cnt = 0; }
  }
  tot = _mm256_add_epi64(tot, _mm256_sad_epu8(acc, z));
  total = hsum256(tot);
  for (; w < Wr; w++) {
    u64 v = bs[0][w];
    for (int d = 1; d < KK; d++) v &= bs[d][w];
    total += (u32)__builtin_popcountll(v);
  }
  if (rem) {
    u64 v = bs[0][Wr];
    for (int d = 1; d < KK; d++) v &= bs[d][Wr];
    total += (u32)__builtin_popcountll(v & ((1ull << rem) - 1));
  }
  return total;
}

template<int KK>
__attribute__((target("avx2,popcnt")))
static void query_group(u32 N, u32 *out, u32 ds, u32 q0, u32 q1, u32 Q, u32 W) {
  const int k = KK;
  const int K1 = KK - 1;
#if MF_ASCR
    // ---- pass A locality: order the group by the bucket in one non-fold dim so that the
    //      ~Q-run of queries needing array (e1,b) is processed together, and pin that
    //      array's prefix in a small scratch (the other k-2 dims still stream from DRAM
    //      and evict a 70 KB hot array within one query, which is why merely sorting
    //      the queries was measured a wash). ----
    const u32 d1 = (ds == 0) ? 1u : 0u;
    const u32 e1 = (d1 < ds) ? d1 : d1 - 1;
    {
      u32 *cntv = s_cntv;
      for (u32 v = 0; v <= Q; v++) cntv[v] = 0;
      for (u32 qi = q0; qi < q1; qi++) {
#if MF_PFB > 0
        if (qi + MF_PFB < q1) _mm_prefetch((const char *)(s_bd + (size_t)s_qord[qi + MF_PFB] * MAXK + d1), _MM_HINT_T0);
#endif
        u32 bk = s_bd[(size_t)s_qord[qi] * MAXK + d1];   /* [B1X-BKT] the only random s_bd read */
        s_bkt[qi] = bk;
        cntv[bk + 1]++;
      }
      { u32 ac = q0; for (u32 v = 0; v <= Q; v++) { u32 c = cntv[v]; cntv[v] = ac; ac += c; } }
      for (u32 qi = q0; qi < q1; qi++) { u32 id = s_qord[qi]; u32 p = cntv[s_bkt[qi] + 1]++; s_tmp[p] = id; s_bka[p] = s_bkt[qi]; }
      for (u32 qi = q0; qi < q1; qi++) s_qord[qi] = s_tmp[qi];
    }
#else
    const u32 d1 = 0, e1 = 0;
#endif
    // ---- pass A: aligned AND; queries ordered lexicographically by their bucket vector so
    //      that consecutive queries share bitset arrays (keeps the working set small). ----
    if (g_lexsort) {
      u32 *cntv = s_cntv;
      for (int dd = k - 1; dd >= 0; dd--) {
        if (dd == ds) continue;
        for (u32 v = 0; v <= Q; v++) cntv[v] = 0;
        for (u32 qi = q0; qi < q1; qi++) cntv[s_bd[(size_t)s_qord[qi] * MAXK + dd] + 1]++;
        { u32 ac = q0; for (u32 v = 0; v <= Q; v++) { u32 c = cntv[v]; cntv[v] = ac; ac += c; } }
        for (u32 qi = q0; qi < q1; qi++) { u32 id = s_qord[qi]; s_tmp[cntv[s_bd[(size_t)id * MAXK + dd] + 1]++] = id; }
        for (u32 qi = q0; qi < q1; qi++) s_qord[qi] = s_tmp[qi];
      }
    }
#ifdef PF_DIST
    for (u32 qi = q0; qi < q0 + PF_DIST && qi + PF_DIST < q1; qi++) {
      u32 j = s_qord[qi + PF_DIST];
      const u32 *bdn = s_bd + (size_t)j * MAXK;
      for (int e = 0; e < K1; e++) {
        int d = (e < ds) ? e : e + 1;
        const char *pp = (const char *)(s_bs + ((size_t)e * (Q + 1) + bdn[d]) * W);
        _mm_prefetch(pp, _MM_HINT_T0);
        _mm_prefetch(pp + 64, _MM_HINT_T0);
        _mm_prefetch(pp + 128, _MM_HINT_T0);
      }
    }
#endif
#if MF_ASCR
    {
      u32 qi = q0;
      while (qi < q1) {
        u32 b1 = s_bka[qi];   /* [B1X-BKT] sorted key, no random s_bd chase */
        u32 qe = qi + 1;
        while (qe < q1 && s_bka[qe] == b1) qe++;
        const u64 *bp1 = g_plen_on ? (s_bs + s_pofs[(size_t)e1 * MAXQ + b1])
                                   : (s_bs + ((size_t)e1 * (Q + 1) + b1) * W);
        u32 mx = 1;
        for (u32 t = qi; t < qe; t++) { u32 rr = s_gt[ds][s_qord[t]]; if (rr > mx) mx = rr; }
        u32 nwd = (mx >> 6) + 1;
        if (nwd <= AS_WORDS) {
          const u64 *src = bp1;
          for (u32 w = 0; w < nwd; w++) s_ascr[w] = src[w];
          bp1 = s_ascr;
        }
        for (u32 qq = qi; qq < qe; qq++) {
          u32 i = s_qord[qq];
          const u32 *bd = s_bd + (size_t)i * MAXK;
#if MF_PFA > 0
          {
            u32 qj = qq + MF_PFA;
            if (qj < qe) {
              u32 j = s_qord[qj];
              _mm_prefetch((const char *)(s_bd + (size_t)j * MAXK), _MM_HINT_T0);
              _mm_prefetch((const char *)(s_gt[ds] + j), _MM_HINT_T0);
            }
          }
#endif
          const u32 R = s_gt[ds][i];
          u32 cnt = 0;
          if (R && !g_skip_and) {
            const u64 *bp[MAXK];
            for (int e = 0; e < K1; e++) {
              if (e == (int)e1) bp[e] = bp1;
              else { int d = (e < ds) ? e : e + 1;
                     bp[e] = g_plen_on ? (s_bs + s_pofs[(size_t)e * MAXQ + bd[d]])
                                       : (s_bs + ((size_t)e * (Q + 1) + bd[d]) * W); }
            }
            cnt = and_popcount_range<KK - 1>(bp, R >> 6, R & 63u);
          }
          out[i] = cnt;
        }
        qi = qe;
      }
    }
#else
    for (u32 qi = q0; qi < q1; qi++) {
      u32 i = s_qord[qi];
      const u32 *bd = s_bd + (size_t)i * MAXK;
#if MF_PFA > 0
      {
        u32 qj = qi + MF_PFA;
        if (qj < q1) {
          u32 j = s_qord[qj];
          _mm_prefetch((const char *)(s_bd + (size_t)j * MAXK), _MM_HINT_T0);
          _mm_prefetch((const char *)(s_gt[ds] + j), _MM_HINT_T0);
        }
      }
#endif
#ifdef PF_DIST
      if (qi + PF_DIST < q1) {
        u32 j = s_qord[qi + PF_DIST];
        const u32 *bdn = s_bd + (size_t)j * MAXK;
        for (int e = 0; e < K1; e++) {
          int d = (e < ds) ? e : e + 1;
          const char *pp = (const char *)(s_bs + ((size_t)e * (Q + 1) + bdn[d]) * W);
          _mm_prefetch(pp, _MM_HINT_T0);
          _mm_prefetch(pp + 64, _MM_HINT_T0);
          _mm_prefetch(pp + 128, _MM_HINT_T0);
        }
      }
#endif
      const u32 R = s_gt[ds][i];
      u32 cnt = 0;
      if (R && !g_skip_and) {
          const u64 *bp[MAXK];
        for (int e = 0; e < K1; e++) {
          int d = (e < ds) ? e : e + 1;
          bp[e] = g_plen_on ? (s_bs + s_pofs[(size_t)e * MAXQ + bd[d]])
                            : (s_bs + ((size_t)e * (Q + 1) + bd[d]) * W);
#ifdef DBG_PLEN
          if (g_plen_on) {
            u32 need = (R >> 6) + ((R & 63) ? 1 : 0);
            if (need > s_plen[(size_t)e * MAXQ + bd[d]])
              printf("DBG ds=%d e=%d d=%d b=%u R=%u need=%u len=%u\n", ds, e, d, bd[d], R, need, s_plen[(size_t)e * MAXQ + bd[d]]);
          }
#endif
        }
        cnt = and_popcount_range<KK - 1>(bp, R >> 6, R & 63u);
      }
      out[i] = cnt;
    }
#endif
    if (g_skip_fringe) return;
    // Reuse each query's bucket-boundary vector across fringe dimensions.
    static u32 *avpool = 0; static size_t avcap = 0;
    {
      size_t needav = (size_t)N * STRIDE * 4 + 256;
      if (needav > avcap) { avpool = (u32 *)pool_alloc(needav); avcap = avpool ? needav : 0; }
      if (avpool) for (u32 qi = q0; qi < q1; qi++) {
        u32 ii = s_qord[qi];
        const u32 *bdi = s_bd + (size_t)ii * MAXK;
        u32 *av = avpool + (size_t)ii * STRIDE;
        for (int e = 0; e < k; e++) av[e] = (e == (int)ds) ? 0x7FFFFFFFu : BVAL(e, bdi[e]);
        for (int e = k; e < STRIDE; e++) av[e] = 0x7FFFFFFFu;
      }
    }
    // ---- pass B: fringe, dimension-major.  Ordering the group's queries by their bucket in
    //      dim d makes every scan walk one L1-resident bucket block of the dim-d order. ----
    for (int d = 0; d < k; d++) {
      if (d == ds) continue;
#if !MF_FR_NOSORT
      /* [NODUP] the first dim this loop reaches is exactly d1, whose stable sort the MF_ASCR
         prologue already performed (and nothing writes s_qord in between), so re-sorting is a
         bit-identical no-op.  One guard skips it; the sort for every later dim is untouched. */
#if !MF_FRORD
      if (d != (int)d1) {
        u32 *cntv = s_cntv;
        for (u32 v = 0; v <= Q; v++) cntv[v] = 0;
        for (u32 qi = q0; qi < q1; qi++) {
#if MF_PFB > 0
          if (qi + MF_PFB < q1) _mm_prefetch((const char *)(s_bd + (size_t)s_qord[qi + MF_PFB] * MAXK + d), _MM_HINT_T0);
#endif
          u32 bb = s_bd[(size_t)s_qord[qi] * MAXK + d];
          s_bkt[qi] = bb;
          cntv[bb + 1]++;
        }
        { u32 ac = q0; for (u32 v = 0; v <= Q; v++) { u32 c = cntv[v]; cntv[v] = ac; ac += c; } }
        for (u32 qi = q0; qi < q1; qi++) {
          u32 id = s_qord[qi];
          s_tmp[cntv[s_bkt[qi] + 1]++] = id;
        }
        for (u32 qi = q0; qi < q1; qi++) s_qord[qi] = s_tmp[qi];
      }
#endif
#endif
      u32 exmask = 0;
      for (int e = 0; e < d; e++) if (e != ds) exmask |= 1u << e;
      // "don't care" lanes are forced to a maximum-unsigned value, so one unsigned max test
      // per candidate decides domination *and* the exclusion rule together.
      u32 hiA[8], hiAh[8];
      for (int e = 0; e < 8; e++) hiA[e] = ((exmask >> e) & 1u) ? 0u : 0x7FFFFFFFu;
      for (int e = 8; e < 16; e++) hiAh[e - 8] = ((exmask >> e) & 1u) ? 0u : 0x7FFFFFFFu;
      const __m256i notexlo = LDU((const __m256i *)hiA);
      const __m256i notexloh = LDU((const __m256i *)hiAh);
      const __m256i all8 = _mm256_set1_epi32(-1);
      const u32 *const odd = s_ord[d];
      const u32 *const sdmpd = s_sdmp[d];
#if MF_FRORD
      const u32 *const fo = s_frord[d];
#endif
      for (u32 qi = q0; qi < q1; qi++) {
#if MF_FRORD
        const u32 pkq = fo[qi];
        u32 i = pkq & 0xFFFFFu;
#else
        u32 i = s_qord[qi];
#endif
#if MF_PFB > 0
        {
          u32 qj = qi + MF_PFB;
          if (qj < q1) {
#if MF_FRORD
            u32 j = fo[qj] & 0xFFFFFu;
#else
            u32 j = s_qord[qj];
#endif
            _mm_prefetch((const char *)(s_pm + (size_t)j * STRIDE), _MM_HINT_T0);
            _mm_prefetch((const char *)(s_gt[d] + j), _MM_HINT_T0);
#if !MF_FRORD
            _mm_prefetch((const char *)(s_bd + (size_t)j * MAXK + d), _MM_HINT_T0);
#endif
            _mm_prefetch((const char *)(avpool + (size_t)j * STRIDE), _MM_HINT_T0);
            __asm__ __volatile__("prefetchw %0" :: "m"(*(char *)(out + j)));
          }
        }
#endif
#if MF_FRORD
        const u32 lo = FPOS(d, pkq >> 20);
#else
        const u32 *bd = s_bd + (size_t)i * MAXK;
        const u32 lo = FPOS(d, bd[d]);
#endif
        const u32 hi = s_gt[d][i];
        if (lo >= hi) continue;
        const u32 *rec = s_pm + (size_t)i * STRIDE;
        const u32 *Avp = avpool + (size_t)i * STRIDE;
        __m256i qr = _mm256_or_si256(LDA((const __m256i *)rec), khi8v);
        __m256i Avm = _mm256_or_si256(LDU((const __m256i *)Avp), notexlo);
        __m256i qT = _mm256_min_epi32(qr, Avm);
        __m256i qTh;
        if (k > 8) {
          __m256i qrh = _mm256_or_si256(LDU((const __m256i *)(rec + 8)), khi8hv);
          __m256i Avmh = _mm256_or_si256(LDU((const __m256i *)(Avp + 8)), notexloh);
          qTh = _mm256_min_epi32(qrh, Avmh);
        } else {
          qTh = all8;
        }
        u32 add = 0;
        const u32 *c0 = sdmpd ? (sdmpd + (size_t)lo * MF_KS_CONST) : 0;
        if (g_use_filt) {
          const u32 w0=lo>>6;
          const u32 nw=((hi-1)>>6)-w0+1;
          const u32 csh=(u32)g_cshift, cQc=(u32)g_Qc;
          const u32 FW=g_fbm_W;
          const u64 *rows[KK-1];
          int nr=0;
          for (int e=0;e<k;e++) {
            if (e==d) continue;
            u32 c=(u32)(((u64)rec[e]*cQc)>>csh)+1;
            if (c>cQc) c=cQc;
            rows[nr++]=s_fbm+((size_t)(d*k+e)*(g_Qc+1)+c)*FW+w0;
          }
          for (u32 w=0;w<nw;w++) {
            u64 vv=~0ull;
            for (int e=0;e<K1;e++) vv &= rows[e][w];
            if (w==0) vv &= ~0ull << (lo&63);
            if (w+1==nw && (hi&63u)) vv &= (1ull<<(hi&63u))-1ull;
#if MF_FR_NOCAND
            vv=0;
#endif
            while (vv) {
              int t = __builtin_ctzll(vv);
              vv &= vv - 1;
              u32 j = ((w0 + w) << 6) + (u32)t;
              const u32 *cp = c0 ? (c0 + (size_t)(j - lo) * MF_KS_CONST) : (s_pm + (size_t)s_ord[d][j] * STRIDE);
              __m256i pv = LDU((const __m256i *)cp);
              u32 ok = (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, pv), all8);
              if (k > 8) {
                __m256i pvh = LDU((const __m256i *)(cp + 8));
                ok &= (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qTh, pvh), all8);
              }
              add += ok;
            }
          }
          out[i] += add;
          continue;
        }
        if (c0) {
          if (k <= 8) {
            const u32 *c = c0;
            u32 a0 = 0, a1 = 0, a2 = 0, a3 = 0;
            u32 j = lo;
            for (; j + 4 <= hi; j += 4) {
              __m256i p0 = LDU((const __m256i *)(c));
              __m256i p1 = LDU((const __m256i *)(c + MF_KS_CONST));
              __m256i p2 = LDU((const __m256i *)(c + 2 * MF_KS_CONST));
              __m256i p3 = LDU((const __m256i *)(c + 3 * MF_KS_CONST));
              a0 += (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, p0), all8);
              a1 += (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, p1), all8);
              a2 += (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, p2), all8);
              a3 += (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, p3), all8);
              c += 4 * MF_KS_CONST;
            }
            for (; j < hi; j++, c += MF_KS_CONST) {
              __m256i pv = LDU((const __m256i *)c);
              a0 += (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, pv), all8);
            }
            add = a0 + a1 + a2 + a3;
          } else {
            const u32 *c = c0;
            for (u32 j = lo; j < hi; j++, c += MF_KS_CONST) {
              __m256i pv = LDU((const __m256i *)c);
              __m256i pvh = LDU((const __m256i *)(c + 8));
              u32 ok = (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, pv), all8) &
                       (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qTh, pvh), all8);
              add += ok;
            }
          }
        } else {
          for (u32 j = lo; j < hi; j++) {
            const u32 *c = s_pm + (size_t)odd[j] * STRIDE;
            __m256i pv = LDA((const __m256i *)c);
            add += (u32)_mm256_testc_si256(_mm256_cmpgt_epi32(qT, pv), all8);
          }
        }
        out[i] += add;
      }
    }
}

__attribute__((target("avx2")))
static void solve_mf(u32 N, const unsigned **x, u32 *out, int k) {
  // W padded to 8 words: every (dim,bucket) array then starts on a 64-byte
  // boundary and every 32-byte vector load inside a stream is line-contained.
  const u32 W = ((N + 63) / 64 + 7u) & ~7u;
  g_W = W;
#ifdef MF_SKIP_AND
  g_skip_and = 1;
#endif
#ifdef MF_FILT
  g_use_filt = 1;
#endif
#ifdef MF_NOFILT
  g_use_filt = 0;
#endif
#ifdef MF_Q
  g_Q = MF_Q; g_B = (u32)((N + (MF_Q) - 1) / (MF_Q));
#endif
#ifdef MF_FILT
  g_use_filt = 1;
#endif
#ifdef MF_NOFILT
  g_use_filt = 0;
#endif
#ifdef MF_SKIP_FRINGE
  g_skip_fringe = 1;
#endif
  if (!g_Q) {
    // bucket size ~ 0.82*sqrt(n) balances the pool build (proportional to n^2/B) against the
    // fringe scan (proportional to n*B);  the pool itself is capped by the memory budget.
    u64 r = (u64)N, s2 = 0;
    while (s2 * s2 < r) s2++;                       // ceil(sqrt(n)) without libm
    // Reserve: the .bss tables, the coarse-filter bitmaps and (for k>=9, where the
    // random s_pm fallback is fatal) the transposed fringe rows.
    u64 overhead = (u64)(5 * k + 21) * N * 4;
    overhead += (u64)k * k * 4 * N;
    overhead += (u64)N * ((k <= 8) ? 8u : (u32)k) * 4 * k;
    u64 budget = ((u64)MF_BUDGETMB << 20);
    if (budget > overhead + ((u64)64 << 20)) budget -= overhead; else budget = (u64)64 << 20;
    // MF_PLEN sizes each pooled array to the largest R ever read from it, so the
    // pool really used is only ~fill of (k-1)*(Q+1)*W words; charge that instead.
#if MF_PLEN
    static const u32 kfill[7] = {72, 67, 62, 58, 54, 50, 46};   // per-array fill, k=4..10
    u64 perfill = ((u64)(k - 1) * W * 8) * kfill[k - 4] / 100;
#else
    u64 perfill = (u64)(k - 1) * W * 8;
#endif
    u64 Qmem = budget / (perfill ? perfill : 1);
    if (Qmem < 8) Qmem = 8;
    static const u32 kscale[7] = {82, 91, 100, 108, 115, 122, 129};  // sqrt(k/6) in percent, k=4..10
    u32 q = (u32)((s2 * MF_QCOEF + 99) / 100);
    q = (u32)(((u64)q * kscale[k - 4] + 99) / 100);
    if ((u64)q > Qmem) q = (u32)Qmem;
    if (q > N) q = N;
    g_Q = q;
    g_B = (N + q - 1) / q;
  }
  const u32 Q = g_Q, B = g_B;
  const u64 bmag = ((1ull << 40) + B - 1) / B;
#if MF_PLEN
  g_plen_on = 1;   // independent of MF_WTRIM: lengths only, stride stays W
#endif
#if MF_WTRIM
  u32 Wpool = W;   // MF_WTRIM is KNOWN-BROKEN, see the warning above
#else
  const u32 Wpool = W;
#endif
#if MF_FILTAUTO
  if (!g_use_filt && B >= 400) g_use_filt = 1;   // coarse grid: the filter beats the scan
#endif
  if (g_use_filt && (u64)k * k * (g_Qc + 1) * W * 8 > ((u64)700 << 20)) g_use_filt = 0;
#if !MF_FILTAUTO
  g_use_filt = 0;
#endif
  {
    int lo8[8], hi8[8];
    for (int e = 0; e < 8; e++) lo8[e] = (e < k) ? 0 : 0x7FFFFFFF;
    for (int e = 0; e < 8; e++) hi8[e] = (8 + e < k) ? 0 : 0x7FFFFFFF;
    khi8v = LDU((const __m256i *)lo8);
    khi8hv = LDU((const __m256i *)hi8);
  }
  const int K1 = k - 1;
  // ---------- per-dimension orders, ranks, tie info ----------
  for (int d = 0; d < k; d++) {
    const unsigned *xd = x[d];
    u32 *od = s_ord[d];
    // Counting sort + rank record in sequential passes: start[v] = #{q : x_d[q] < v}
    // is exactly gt[] of every point with x_d == v, and end[v]-1 = start[v+1]-1 is the
    // last rank of that tie group, so bd[] follows from start[v+1] too.  Both are value
    // lookups, so the per-point work is a sequential sweep of xd[] instead of the
    // previous random xd[od[j]] read plus random gt[]/bd[] writes.
    for (u32 i = 0; i <= N; i++) s_cntv[i] = 0;
    for (u32 i = 0; i < N; i++) { u32 v = xd[i]; s_cntv[v < N ? v : 0]++; }
    { u32 s = 0; for (u32 i = 0; i <= N; i++) { u32 c = s_cntv[i]; s_cntv[i] = s; s_tmp[i] = s; s += c; } }
    { u32 *pp = s_pos[d];
      for (u32 i = 0; i < N; i++) { u32 v = xd[i]; if (v >= N) v = 0; u32 q = s_tmp[v]++; od[q] = i; pp[i] = q; } }
    { u32 *gt = s_gt[d];
#if MF_FRORD
      /* bucket columns as u16, parked in the half of s_posp that this build never writes
         (s_posp is only written under MF_FB2, which is 0 in every shipped config). */
      unsigned short *zcol = (unsigned short *)(s_posp + 2u * (size_t)MAXN) + (size_t)d * ((size_t)N + 64);
#endif
      for (u32 i = 0; i < N; i++) {
        u32 v = xd[i]; if (v >= N) v = 0;
        gt[i] = s_cntv[v];
        u32 num = s_cntv[v + 1] - 1u; if (num > N) num = N;
        u32 bb = (u32)(((u64)num * bmag) >> 40);
        if (bb > Q - 1) bb = Q - 1;
        s_bd[(size_t)i * MAXK + d] = bb;
#if MF_FRORD
        zcol[i] = (unsigned short)bb;
#endif
      } }
    BVAL(d, 0) = 0; FPOS(d, 0) = 0;
    for (u32 b = 1; b < Q; b++) {
      u32 pos = b * B < N ? b * B : N - 1;
      u32 v = xd[od[pos]];
      BVAL(d, b) = v;
      u32 gs = pos;
      while (gs > 0 && xd[od[gs - 1]] == v) gs--;
      FPOS(d, b) = gs;
    }
    BVAL(d, Q) = 0xFFFFFFFFu; FPOS(d, Q) = 0;
  }
  for (u32 i = 0; i < N; i++) {
    u32 *p = s_pm + (size_t)i * STRIDE;
    for (int d = 0; d < k; d++) p[d] = x[d][i];
    for (int d = k; d < STRIDE; d++) p[d] = 0;
  }
  // ---------- coarse superset bitmaps over every dim order (fringe filter) ----------
#if MF_NOFILTB
  g_use_filt = 0;
#endif
  // [W2] Placed AFTER every write to g_use_filt that precedes the s_bnde ladder, and
  // BEFORE the first CBOUND() evaluation: both the boundary ladder and the per-query
  // cell index must use the SAME shift (see ref [6]).
  if (g_use_filt) { int sh = 0; while ((sh < 32) && (((u64)1 << sh) < (u64)N)) sh++; g_cshift = sh; }
#if MF_FB3
  if (g_use_filt) {
    u32 Qc = g_Qc;
    for (int e = 0; e < k; e++) {
      const unsigned *xe = x[e];
      const u32 *pe = s_ord[e];
      u32 ptr = 0;
      u32 *bn = s_bnde + (size_t)e * (MAXQ + 2);
      for (u32 c = 0; c <= Qc; c++) {
        u64 v = CBOUND(c);
        if (v > 0xFFFFFFFFull) v = 0xFFFFFFFFull;
        while (v && ptr < N && (u64)xe[pe[ptr]] < v) ptr++;
        bn[c] = ptr;
      }
    }
  }
#endif
  if (g_use_filt) {
    u32 Qc = g_Qc;
    u64 wc = (u64)N / Qc + 1;
    size_t need2 = (size_t)k * k * (Qc + 1) * W * sizeof(u64);
    static u64 *fpool = 0; static size_t fcap = 0;
    if (need2 > fcap) { fpool = (u64 *)pool_alloc(need2 + 4096); fcap = need2; }
    if (fpool) {
      s_fbm = fpool; g_fbm_W = W;
#if MF_FB2
      // Sweep the e-order ONCE per dim e, keeping (k-1) running bitsets (one per
      // target dim d), and copy them out at each coarse-cell boundary.  The old
      // shape re-walked all N points for every one of the k(k-1) (d,e) pairs with
      // two L3-random reads each (xe[pe[ptr]] and s_pos[d][pe[ptr]]); here the
      // rank record is point-major so one 64B line yields every dim's rank, which
      // cuts the random reads by ~k and the whole build by ~2-2.5x at k>=8.
      for (int d = 0; d < k; d++)
        for (int e = 0; e < k; e++) {
          if (e == d) continue;
          u64 *b0 = s_fbm + (size_t)(d * k + e) * (Qc + 1) * W;
          for (u32 w = 0; w < W; w++) b0[w] = 0;
        }
      for (int e = 0; e < k; e++) {
        const unsigned *xe = x[e];
        const u32 *pe = s_ord[e];
        int dmap[MAXK], km1 = 0;
        for (int d = 0; d < k; d++) if (d != e) dmap[km1++] = d;
        for (int i = 0; i < km1; i++) { u64 *a = s_facc + (size_t)i * W; for (u32 w = 0; w < W; w++) a[w] = 0; }
        u32 ptr = 0;
        for (u32 c = 1; c <= Qc; c++) {
          u64 v = (u64)c * wc;
          if (v > 0xFFFFFFFFull) v = 0xFFFFFFFFull;
          while (ptr < N && (u64)xe[pe[ptr]] < v) {
            const u32 *pv = s_posp + (size_t)pe[ptr] * MAXK;
            for (int i = 0; i < km1; i++) {
              u32 q = pv[dmap[i]];
              s_facc[(size_t)i * W + (q >> 6)] |= 1ull << (q & 63);
            }
            ptr++;
          }
          for (int i = 0; i < km1; i++) {
            int d = dmap[i];
            u64 *dst = s_fbm + ((size_t)(d * k + e) * (Qc + 1) + c) * W;
            const u64 *src = s_facc + (size_t)i * W;
#if MF_NT
            { u32 w = 0;
              for (; w + 4 <= W; w += 4) _mm256_stream_si256((__m256i *)(dst + w), LDA((const __m256i *)(src + w)));
              for (; w < W; w++) dst[w] = src[w]; }
#else
            for (u32 w = 0; w < W; w++) dst[w] = src[w];
#endif
          }
        }
      }
#else
      for (int d = 0; d < k; d++) {
        for (int e = 0; e < k; e++) {
          if (e == d) continue;
          const unsigned *xe = x[e];
          const u32 *pe = s_ord[e];
          const u32 *pd = s_pos[d];
          u64 *base = s_fbm + (size_t)(d * k + e) * (Qc + 1) * W;
          for (u32 w = 0; w < W; w++) base[w] = 0;
          u64 *cur = s_acc;
          for (u32 w = 0; w < W; w++) cur[w] = 0;
          u32 ptr = 0;
          for (u32 c = 1; c <= Qc; c++) {
#if MF_FB3
            u32 pend = s_bnde[(size_t)e * (MAXQ + 2) + c];
#else
            u64 v = (u64)c * wc;
            if (v > 0xFFFFFFFFull) v = 0xFFFFFFFFull;
            u32 pend = ptr;
            { const unsigned *xe_ = xe; const u32 *pe_ = pe;
              while (pend < N && (u64)xe_[pe_[pend]] < v) pend++; }
#endif
            while (ptr < pend) { u32 q = pd[pe[ptr]]; cur[q >> 6] |= 1ull << (q & 63); ptr++; }
            u64 *dst = base + (size_t)c * W;
#if MF_NT
            { u32 w = 0;
              for (; w + 4 <= W; w += 4) _mm256_stream_si256((__m256i *)(dst + w), LDA((const __m256i *)(cur + w)));
              for (; w < W; w++) dst[w] = cur[w]; }
#else
            for (u32 w = 0; w < W; w++) dst[w] = cur[w];
#endif
          }
        }
      }
#endif
    } else { g_use_filt = 0; }
    _mm_sfence();
  }

  // ---------- fold dimension + bucket vector per query ----------
  {
    size_t mrn = (size_t)MAXK * MAXK * MAXQ;
    for (size_t z = 0; z < mrn; z++) s_maxr[z] = 0;
  }
  for (u32 i = 0; i < N; i++) {
    u32 best = 0xFFFFFFFFu; int ds = 0;
    for (int d = 0; d < k; d++) {
      u32 g = s_gt[d][i];
      if (g < best) { best = g; ds = d; }
    }
    s_g[i] = (u32)ds;
    // Largest R that will ever be read out of each (fold dim, dim, bucket) array:
    // the pooled array (ds,d,b) is only ever scanned over [0, maxR] words, so the
    // per-array length can be far below the group maximum (0.38n vs 0.93n at k=10).
    if (g_plen_on) {
      const u32 *bdv = s_bd + (size_t)i * MAXK;
      u32 *mr = s_maxr + ((size_t)ds * MAXK) * MAXQ;
      u32 bb = best;
      for (int d = 0; d < k; d++) {
        if (d == ds) continue;
        u32 b = bdv[d];
        if (bb > mr[(size_t)d * MAXQ + b]) mr[(size_t)d * MAXQ + b] = bb;
      }
    }
  }
  for (u32 i = 0; i < N; i++) s_qord[i] = i;
  // group by ds
  {
    for (u32 v = 0; v <= (u32)k; v++) s_cntv[v] = 0;
    for (u32 i = 0; i < N; i++) s_cntv[s_g[i] + 1]++;
    { u32 ac = 0; for (u32 v = 0; v <= (u32)k; v++) { u32 c = s_cntv[v]; s_cntv[v] = ac; ac += c; } }
    for (u32 i = 0; i < N; i++) { u32 id = s_qord[i]; s_tmp[s_cntv[s_g[id] + 1]++] = id; }
    for (u32 i = 0; i < N; i++) s_qord[i] = s_tmp[i];
  }
  u32 gstart[MAXK + 2];
  { u32 c = 0; for (int d = 0; d <= k; d++) { gstart[d] = c; while (c < N && (int)s_g[s_qord[c]] == d) c++; } gstart[k + 1] = c; }
#if MF_FRORD
  /* One pass per dim: walk the dim-d order and drop each point into its fold group's
     segment, carrying the dim-d bucket alongside (single u32). */
  for (int d = 0; d < k; d++) {
    u32 pos[MAXK];
    for (int ds2 = 0; ds2 < k; ds2++) pos[ds2] = gstart[ds2];
    const unsigned short *zcol = (const unsigned short *)(s_posp + 2u * (size_t)MAXN) + (size_t)d * ((size_t)N + 64);
    const u32 *od2 = s_ord[d];
    u32 *fo2 = s_frord[d];
    for (u32 r = 0; r < N; r++) {
      u32 id = od2[r];
      u32 dst = pos[s_g[id]]++;
      fo2[dst] = id | ((u32)zcol[id] << 20);
    }
  }
#endif
  // ---------- transposed coordinate rows (sequential fringe) ----------
#if MF_NOSDMP
  g_nosdmp = 1;
#endif
  if (!g_nosdmp) {
    u32 KS = k;
#if MF_KSPAD
    if (k <= 8) KS = 8;          // 32-byte aligned candidate rows for k<=8
#endif
    size_t per = (size_t)N * KS * 4;
    /* [e6x] raised from 150 MB: at n=1e6, k=6 the old gate saw per*k = 192 MB and left pass B
       on the MLP=1 random-probe chain (`s_pm + odd[j]*STRIDE`, one un-prefetched miss per
       candidate).  With the rows built, pass B takes the contiguous 4x-unrolled branch with
       four independent accumulators.  Same min-fold arithmetic; +192 MB. */
    size_t lim = (size_t)470 << 20;
    if (per * k <= lim) {
      static u32 *pool = 0; static size_t cap = 0;
      if (per * k > cap) { pool = (u32 *)pool_alloc(per * k + 256); cap = per * k; }
      if (pool) {
        g_KS = KS;
        for (int d = 0; d < k; d++) {
          s_sdmp[d] = pool + (size_t)d * N * g_KS;
          const u32 *od = s_ord[d];
          u32 *dst = s_sdmp[d];
          const u32 KSx = g_KS;
          for (u32 j = 0; j < N; j++) {
#if MF_PFB > 0
            if (j + MF_PFB < N) _mm_prefetch((const char *)(s_pm + (size_t)od[j + MF_PFB] * STRIDE), _MM_HINT_T0);
#endif
            const u32 *row = s_pm + (size_t)od[j] * STRIDE;
            u32 *dp = dst + (size_t)j * KSx;
            for (u32 e = 0; e < KSx; e++) dp[e] = (e < (u32)k) ? row[e] : 0u;
          }
        }
      }
    }
  }
  // ---------- pool ----------
  u64 poolwords = (u64)K1 * (Q + 1) * W;
#if MF_PLEN
  if (g_plen_on) {
    u64 best = 0;
    for (int dsx = 0; dsx < k; dsx++) {
      u64 tot = 0;
      for (int e = 0; e < K1; e++) {
        int d = (e < dsx) ? e : e + 1;
        const u32 *mr = s_maxr + ((size_t)dsx * MAXK + d) * MAXQ;
        for (u32 b = 0; b <= Q; b++) {
          u32 RR = mr[b];
          u32 len = ((RR >> 6) + 8u) & ~7u;
          if (len > W) len = W;
          tot += len;
        }
      }
      if (tot > best) best = tot;
    }
    if (best && best < poolwords) poolwords = best;
  }
#endif
  size_t need = (size_t)poolwords * sizeof(u64);
  static u64 *pool = 0; static size_t cap2 = 0;
  if (need > cap2) { pool = (u64 *)pool_alloc(need + 4096); cap2 = need; }
  s_bs = pool;
  // ---------- groups ----------
  for (int ds = 0; ds < k; ds++) {
    u32 q0 = gstart[ds], q1 = gstart[ds + 1];
    if (q0 >= q1) continue;
#if MF_WTRIM
    {
      u32 mx = 1;
      for (u32 qi = q0; qi < q1; qi++) { u32 rr = s_gt[ds][s_qord[qi]]; if (rr > mx) mx = rr; }
      u32 wp = ((mx >> 6) + 8u) & ~7u;
      Wpool = (wp < W) ? wp : W;
    }
#endif
#if MF_PLEN
    // Per-(dim,bucket) array lengths inside the group.
    if (g_plen_on) {
      u64 tot = 0;
      for (int e = 0; e < K1; e++) {
        int d = (e < ds) ? e : e + 1;
        const u32 *mr = s_maxr + ((size_t)ds * MAXK + d) * MAXQ;
        u32 *po = s_pofs + (size_t)e * MAXQ;
        u32 *pl = s_plen + (size_t)e * MAXQ;
        for (u32 b = 0; b <= Q; b++) {
          // NOTE: bucket 0 is the EMPTY prefix set (all-zero array), but a query whose
          // bd[d]==0 still scans [0,R) words of it, so the array must be long enough --
          // sizing it to the fixed 8-word minimum reads past the zeroed region into the
          // next array.  Use the real per-bucket max R here, not 0.
          u32 RR = mr[b];
          u32 len = ((RR >> 6) + 8u) & ~7u;
          if (len > Wpool) len = Wpool;
          po[b] = (u32)tot; pl[b] = len; tot += len;
        }
      }
      if (tot > (u64)K1 * (Q + 1) * W) { g_plen_on = 0; }   // never; safety
    }
#endif
    // bit numbering = rank in dim ds
    {
      const u32 *od = s_ord[ds];
      for (u32 j = 0; j < N; j++) s_nid[od[j]] = j;
    }
    for (int e = 0; e < K1; e++) {
      int d = (e < ds) ? e : e + 1;
      const unsigned *xd = x[d];
      const u32 *od = s_ord[d];
      u64 *base = g_plen_on ? s_bs : (s_bs + (size_t)e * (Q + 1) * Wpool);
      u64 *cur = s_acc;
      for (u32 w = 0; w < Wpool; w++) cur[w] = 0;
      if (g_plen_on) {
        // bucket 0 is the empty prefix set; bd[] can be 0 so it must be materialised as zeros
        u64 *d0 = s_bs + s_pofs[(size_t)e * MAXQ];
        u32 l0 = s_plen[(size_t)e * MAXQ];
        for (u32 w = 0; w < l0; w++) d0[w] = 0;
      }
      u32 ptr = 0;
#if MF_BUILD_NOPOOL
      while (ptr < N) ptr++;
#else
      for (u32 b = 1; b <= Q; b++) {
        // {q : x_d[q] < BVAL(d,b)} is EXACTLY the prefix [0,FPOS(d,b)) of the dim-d order
        // (FPOS is the tie-group start containing rank b*B), so the per-point value compare
        // -- one random xd read per point per (ds,d) pair, k(k-1)N of them -- is redundant.
        // b==Q means "every point" and FPOS(d,Q) is the 0 sentinel, hence N.
        // Credited to agent a7f799ceb7e63b0cd (worth 4-5% at k=4/5 on the board).
        u32 pend = (b < Q) ? FPOS(d, b) : (u32)N;
        while (ptr < pend) { u32 q = s_nid[od[ptr]]; cur[q >> 6] |= 1ull << (q & 63); ptr++; }
        u64 *dst = g_plen_on ? (base + s_pofs[(size_t)e * MAXQ + b])
                              : (base + (size_t)b * Wpool);
        u32 wlen = g_plen_on ? s_plen[(size_t)e * MAXQ + b] : Wpool;
#if MF_NT
        // The pool is written once and read 89 GB later, so a plain copy pays a
        // read-for-ownership DRAM read per line for nothing.  NT stores remove it
        // (measured: this loop was 4.58 GB / ~1.7 s, i.e. 2.7 GB/s, vs 12.5 GB/s
        // for a plain memset).
        {
          u32 w = 0;
          for (; w + 4 <= wlen; w += 4)
            _mm256_stream_si256((__m256i *)(dst + w), LDA((const __m256i *)(cur + w)));
          for (; w < wlen; w++) dst[w] = cur[w];
        }
#else
        for (u32 w = 0; w < wlen; w++) dst[w] = cur[w];
#endif
      }
      _mm_sfence();
#endif
    }
    switch (k) {
      case 4: query_group<4>(N, out, ds, q0, q1, Q, Wpool); break;
      case 5: query_group<5>(N, out, ds, q0, q1, Q, Wpool); break;
      case 6: query_group<6>(N, out, ds, q0, q1, Q, Wpool); break;
      default: query_group<6>(N, out, ds, q0, q1, Q, Wpool); break;
    }
  }
}

void count_4d(int n, const unsigned *x[4], unsigned *out) { solve_mf((u32)n, (const unsigned **)x, out, 4); }
void count_5d(int n, const unsigned *x[5], unsigned *out) { solve_mf((u32)n, (const unsigned **)x, out, 5); }
void count_6d(int n, const unsigned *x[6], unsigned *out) { solve_mf((u32)n, (const unsigned **)x, out, 6); }

CompilationN/AN/ACompile OKScore: N/A

Testcase #14.598 s758 MB + 800 KBAcceptedScore: 100


Judge Duck Online | 评测鸭在线
Server Time: 2026-10-17 08:06:15 | Loaded in 1 ms | Server Status
个人娱乐项目,仅供学习交流使用 | 捐赠