// ===== REFERENCES([o18k2_ 席] 2026-10-03)=====
// [1] 本文件正文 = **我方**提交 **#121694** <https://duck.ac/submission/121694>
// (noi18a,230.844410 ms,Accepted)的逐字节副本;磁盘件 `work/o18p_s1.cpp`。
// 该件的完整世系引用链在其正文头部,本席一字未动,转引如下:
// #121694([o18p_] KRT 归并环第 2 跳预取)← #121586([o18u_] Dijkstra 前瞻 8->2)
// ← #121480([o18v_] 携带式查询环)← #121466 ← #121450 ← #121354([o18w_] 软件流水+两预取)
// ← #121219([o18z_] K 特化)← #121131 ← 对手 **saffah_codex_6a_agg3** 公开件
// **#120969** <https://duck.ac/submission/120969> ← 我方 #120828;
// 其中 K=1 查询臂的**携带结构**出自对手 **saffah_codex_6a_agg3** 公开件
// **#121434** <https://duck.ac/submission/121434>;三处预取位置出自我方 #120303。
// [2] 本席新增的两行(parse 环首行 + K=1 查询环首行的 `_mm_prefetch(..., _MM_HINT_NTA)`)
// 不引自他人;出处 = 本队 [b14ad_] 1014「流-表账」族的**输入流 NTA** 条(同队内部口径)。
// 【许可合规】以上均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18k2_ 席] 2026-10-03)=====
// 【任务】用「流-表账 / 写侧 NT」重估本题的随机表站点——本席盘点 notes.md 全文:
// 「随机访问/随机表」13 次、「NT / vmovntdq / 流-表账」**0 次** ⇒ 整族从未在本题试过。
// 【账】本题 K=1 查询环(= 记分点 tc19/tc20 的臂)就是标准的「顺序索引 → 随机表」环:
// 顺序流 ①**输出游标 op_**:纯存(pu32 只写不读),消费者 = 判题机(solve() 返回后才读)
// ⇒ **不命中 L3** ⇒ 按作用域律属「输出流 NT」面;
// 顺序流 ②**查询输入游标 ip_**:纯读,线被读过即弃(每测试点 ~7.2 MB)⇒ 属「输入流 NTA」面;
// 随机表 = vinfo 1.6 MB + A0 0.8 + dPos 0.8 + preD 0.8 + sufD 0.8 + st1 0.15 ≈ **4.95 MB**
// vs 判题机有效 L3 ≈ 2~4 MB。
// 【判据 (a) 可保性】表 4.95 MB vs L3 2~4 MB:部分可保;而两条顺序流合计 ~8.7 MB/测试点
// **远大于** L3 ⇒ 它们在环内流动时把表整片挤出 ⇒ 把流按各自作用域律出账,即直接给表让出 L3。
// 【判据 (b) 成对读】NT 单上无意义,必须与次序/流处理成对读。
// 【本发刀】输入流两处 `_mm_prefetch(ip_ + 256, _MM_HINT_NTA)`(**已在册的刀**,板面 #123146
// = 229.372690 ms,−1.471720 ms)**+ 本发唯一新变量**:输出目的地的 **RFO-hint**
// `__asm__ volatile("prefetchw %0" :: "m"(op_[128]))`(**必须内联汇编**:gcc 在本机
// 对 `__builtin_prefetch(x,1,·)` 不发射 `prefetchw`,已实测)。
// 【法条】输出行是**纯存 + 被整块写满**(pu32 从 op_+len 倒着写满 [op_, op_+len+1))⇒
// 属 [b14ad_] 1014 的「写目的地内联汇编 prefetchw」适用面;提前发出所有权请求,
// 使该行在字节 store 落位前已在 cache(Exclusive),绕开 L3 压力下的裸露 RFO 延迟。
// 【为什么不是 NT】同族上一发已实测 **NT 反向 +2.041 ms**(#123157)⇒ 本发只动 hint。
// 【读数】判题机板面逐发定价(本机相位仪对存储相位不可信);台账见 notes.md [o18k2_ 席] 节。
// 【闸门】big0 md5 `a25cb5f278edf846f04fee1cfb82f035` ✓ · 与 #121694 随机对拍 **600 例 bad=0** ✓ ·
// 判题机 gcc 9.3 编译 OK ✓(`tools/gcc93check.py`)。
// ===== REFERENCES([o18p_ 席] 2026-10-02)=====
// [1] 本文件正文 = **我方**提交 **#121586** <https://duck.ac/submission/121586>(noi18a,230.935997 ms,AC)
// 的逐字节副本;本席**只改一处**:KRT 并查集归并环的预取深度(见下)。
// #121586 自带头部完整世系引用链(对手 saffah_codex_6a_agg3 #121434/#120969;我方
// #121480/#121354/#121219/#121131/#120828/#109637…),本席未改动该链。
// 【许可合规】以上为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18p_ 席] 2026-10-02;试验性件,目的:把本席派单的"pop 头部"否定后
// 改打 KRT 归并环)=====
// 【派单项判否】k0 任务 = Dijkstra pop 头部 `beg[u]/beg[u+1]` 零前瞻载入。判题机侧探针
// (work/o18p_probe.cpp,6 臂 × 3 轮轮转 + 48 MB 冲刷 + dist_ FNV 校验)实测:
// 同码空对照 d0↔d0b 差 **0.07%**(噪声底);d1 池条目 (next,vertex) 压 u64 +0.2% ·
// d2 relax 处 prefetch beg[v] +0.3% · d3 = d1+d2 +0.2% · **d4 push 时把 [e,e2] 存进池
// +16.3%(池多 6.4 MB ⇒ 触到"该环对多余流量过敏"的红线)** ⇒ **beg 载入不在关键路径上**,
// 该刀整族判否。
// 【本发刀】phase3(KRT) = 60.7 M/组 = 27%(全队从未动过)里,**并查集归并环 ≈41.8 M/组**
// (探针 3 直接计得),100 拍/边 ⇒ 该环是**两条 find 链的第 2/3 跳随机载入**受限:
// 原 `prefetchw dsu[rs[i+8].u/v]` 只预热**第 1 跳**,深跳仍裸奔。
// 本发把第 i+8 条边的**第 1 跳值先载入**(该行已被上一轮 prefetchw 预热 ⇒ 近 L1 命中),
// 再用它 `prefetchw` **第 2 跳** ⇒ 归并时的链由「暖→冷→冷」变「暖→暖→冷」。
// 【判题机侧探针实测(work/o18p_probe3.cpp,5 臂 × 4 轮轮转 + 冲刷 + ch1/ch2 校验和)】
// k0 41,825,808 · **k0b 42,159,484(同码对照)** · **k1 39,397,930(−6.55% vs k0b)** ·
// k2 距离 8→16 41,360,296(−1.9%)· **k3 删预取 45,585,742(+8.1% ⇒ 证明该环确为预取深度敏感)**。
// 全部 5 臂 ch1/ch2 校验和相同 ⇒ 逐位等价 ✓
// 【拍→板面】按本队在册换算 **1 拍/查询 ≈ 0.13 ms**([o18w_])与"链上指令记账"律([o18z_]),
// 本发省 2.74 M 拍/组 × 3 组 ≈ 8.2 M 拍 ≈ **2.3 ms**(≈ 当前缺口 2.290 ms)。
// 另有我方 [o18v_] 实证:**链上**改动板面兑现 ~0.9~1.5×;本发属链延迟族(非投机预取、非换访存形态)。
// 【闸门】big0 md5 / 六输入逐字节 / 随机对拍 / brute —— 见 notes.md [o18p_ 席] 节。
// ===== REFERENCES([o18u_ 席] 2026-10-02)=====
// [1] 本文件正文 = **我方**提交 **#121480** <https://duck.ac/submission/121480>(noi18a,231.123012 ms,AC)
// 的逐字节副本(磁盘件 = `work/o18v_s2.cpp`,其头部自带完整世系引用链,本席未改动);
// 本席**只改一处常数**:Dijkstra 弧扫里 `dist_` 的前瞻预取距离 **8 -> 2**(见下)。
// 【许可合规】以上为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18u_ 席] 2026-10-02)=====
// 【前提】逐用例表(tools/pertest.py:#121480 = tc19 231.123 / tc20 230.477,其余最大 159.5)
// 实证**记分点 = tc19/tc20 且两席均 K=1**;故本席先否掉 [o18v_] 交棒的「携带搬到非 K=1 臂」
// (该臂覆盖的最大用例仅 159.5 ms ⇒ 全改到 0 也不动 max),把额度投到 Dijkstra(判题机相位表 25%)。
// 【刀】现弧扫的预取是 `dist_ + (adj[e+8])` 且只在**行内剩余 >= 8 弧**时生效;而 tc19 形状
// 为 n=2e5/m=4e5(无向)= **平均度 4**(生成器 = 链 n-1 + 随机 2e5)⇒ 绝大多数行的弧数 < 8
// ⇒ **该预取在记分点上基本从不发射**,`dist_[v]`(800 KB 随机)几乎全是零前瞻载入。
// 本发把距离 8 -> 2(仅改常数与配套的 `e8` 边界),使预取在「剩余 >= 2 弧」时即发射。
// 【性质】属「**预取族**」⇒ 按全队律**本机不可定价**(本机与判题机 L3 几何不同:本机 Dijkstra
// 相位噪声 ±5%,同批空对照(相位 3 逐字节同代码)自己就偏 +4~8%)⇒ **只由板面判决**。
// 【闸门】big0 md5 `a25cb5f2…` ✓ · 六输入逐字节同 ✓ · 与 #121480 随机对拍 1512 例 bad=0 ✓ ·
// 与 brute 对拍 60 例 bad=0 ✓ · 判题机 gcc 9.3 编译 OK ✓
// ===== REFERENCES([o18v_ 席] 2026-10-02)=====
// [0] ★本文件 K=1 查询臂**正文结构**抄自对手 duck.ac 用户 **saffah_codex_6a_agg3** 的公开件
// **#121434** <https://duck.ac/submission/121434>(noi18a,230.954822 ms,AC)的查询环:
// 其零答案臂写 `v=nv0; p=np0;`(把两个 fastmod 整段删掉、并携带到下一轮),
// 非零臂写 `v=fastmod(nv0-1+ans); p=fastmod(np0+ans);` 后再预取 `vinfo+v`。
// ★本席只抄这一处**结构**;其余正文 = 我方 #121450 的逐字节副本。
// 本席改动(相对 #121434):v 以 **0-based**(vm1≡v-1)携带 ⇒ 载入写成 `vinfo[vm1+1]`,
// `+1` 折进 disp8 位移 ⇒ 链上少一条 add(**取值逐位恒等**,本地闸门已验)。
// #121434 自身头注声明其正文抄自我方 #121219 <https://duck.ac/submission/121219>,
// 并保留其世系引用(#121131 ← 对手 #120969).
// [1] 本文件正文(除上条所述查询臂)= **我方**提交 **#121450** <https://duck.ac/submission/121450>
// (noi18a,233.667719 ms,AC)的逐字节副本;#121450 = #121354 + [pf-next-v2]([o18w_ 席])。
// 本方世系:#121131(对手 #120969 + [K0])→ #121219([o18z_] K 特化)→ #121354([o18w_] 软件流水+两预取)。
// 【许可合规】以上均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18v_ 席] 2026-10-02)=====
// 【任务】判题机探针实证:查询环是「答案→v→vinfo→compMin」**串行链**受限
// ([o18z_] 四仪器同号、板面只给链上指令的账)。本发先**把链上的两个 fastmod 从 54.2%
// 的零答案迭代里整段拿走**(对手 #121434 的刀,板面实测 −2.921 ms),再叠本席的
// 0-based 携带(链上少一条 add)。
// 【闸门】big0 md5 `a25cb5f278edf846f04fee1cfb82f035` ✓ · K0/K1×{随机图,链,树} 六输入逐字节同 ✓ ·
// 与 #121450 随机对拍 1512 例 bad=0 ✓ · brute 对拍 60 例 bad=0 ✓ · 判题机 gcc 9.3 编译 OK ✓
// 【探针】本席另用立即体验渠道实测判题机:`nproc=1`、`fork()` 被拒、CLOCK_MONOTONIC 不可读
// ⇒ **并行/分核整条线判负**(详见 problems/noi18a/notes.md [o18v_ 席] 节)。
// ===== REFERENCES([o18w_ 席] 2026-10-02)=====
// [1] 本文件正文 = **我方**提交 **#121354** <https://duck.ac/submission/121354>(noi18a,**233.875965 ms**,AC)
// 的逐字节副本 + 一处新增预取(见 (c));#121354 本身 = 我方 #121219
// <https://duck.ac/submission/121219>(235.924026 ms)的逐字节副本 + 本席 K=1 臂改动。
// ★ 本席只改 K=1 查询臂,K=0 臂与正文其余一字未动 ✓
// (#121219 自带头部给出其世系:= 我方 #121131 + [o18z_] K 特化;#121131 = 对手
// **saffah_codex_6a_agg3** 公开件 **#120969** <https://duck.ac/submission/120969> 的逐字节副本 + [K0] 直通)
// [2] 本条三处预取的**位置**取自**我方**既有刀 `[n18az-1][pf-line]` / `[n18az-2][pf-hoist]`
// (正文注释内已标;出处 = 我方 #120303 <https://duck.ac/submission/120303> 的 `work/o18x_p3.cpp`)——
// 本席只把它们的**地址来源**从"当前查询"扩到"下一查询",未引入新的引用源 ✓
// 【许可合规】以上均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18w_ 席] 2026-10-02)=====
// 【任务】缩短查询环「答案→v→vinfo→compMin」的**串行链延迟**(本题唯一在册的正刀族)。
// 【刀】三处**地址确定**的预取(只改时序、不改取值)+ 一处软件流水:
// (a) [pf-next-v] 软件流水:把**下一查询的 (v0,p0) 提前一轮解析**(ni() 调用次数/顺序与原文逐位相同
// ⇒ 取值逐位不变,FNV 校验同值)。于是本轮即可发出下一查询的 vinfo 行预取:题面 K=1 时
// v=(v0+K·lastans−1) mod n + 1 而 **lastans=0 ⇒ v≡v0**(逐用例实测 54.2% 的答案是 0)
// ⇒ 该预取 54.2% 命中、且提前一整轮(≈130 拍)发出。
// (b) [pf-dpos] 答案非 0 时 x 已知 ⇒ 在既有 A0 预取旁同刻预取 **dPos + (x & ~15)**:
// 答案区间 [Lb,Rb] 恒含 x(Lb≤x≤Rb),逐查询实测 **77% 的区间落在 x 的 64B 线内**
// ⇒ dPos 缺失与 A0 扫描**并行**,不再串在其后。
// (c) [pf-next-v2] 答案非 0 时 **ans 即下一查询的 lastans** ⇒ 下一查询的 v 是**精确已知**的
// (v=(nv0−1+ans) mod n + 1,无投机)⇒ compMin 返回后立刻再预取它的 vinfo 行。
// (地址只用于 prefetch,不参与取值;这一条覆盖 (a) 猜不中的那 45.8%。)
// 【依据】判题机侧探针(`work/o18w_probe.cpp`:真引擎 + 程序内自造判题同形输入(gen 同 seed)、
// 臂间 48 MB 冲刷、臂序轮转、rdtsc 计入、FNV 自校验):**同一二进制**上逐臂(Q=80k,min-of-4)
// `q0`(基座)10,926,080 cyc · `qQ`(=a+b) 9,667,458 = **−11.52%** · `qR`(=a+b+c) 9,337,234 = **−14.54%**
// · 空对照臂 `qnull` 10,903,078 = −0.21%(= 该探针噪声底)。分解:`qde`(b) −4.77% · `qp`(a) −5.93%。
// ★ 拍数→板面换算已由**本席自己的封闭实验**标定:#121219→#121354 实测 **−2.048 ms**(tc19 235.924026→233.875965)
// ↔ 探针 −15.7 拍/查询 ⇒ **1 拍/查询 ≈ 0.13 ms**(比 TSC 名义 0.111 ms 高 1.4×,与在册
// [o18z_] K 特化刀的 1.43× 互证 ✓)。
// 【闸门】六输入逐字节同(K=0/K=1 × {随机图,链,树})✓ · big0 输出 md5 `a25cb5f278edf846f04fee1c…` ✓ ·
// 与基座随机对拍 400 例 bad=0 ✓ · 与 brute 对拍 60 例 bad=0 ✓ · 判题机 gcc 9.3 编译 OK ✓
// ===== REFERENCES([o18z_ 席] 2026-10-02)=====
// [1] 本文件正文 = **我方**提交 **#121131** <https://duck.ac/submission/121131> 的逐字节副本
// (该件 = 对手 duck.ac 用户 **saffah_codex_6a_agg3** 的公开件 **#120969**
// <https://duck.ac/submission/120969>(236.441695 ms,赛时 T)的逐字节副本
// + 我方 [K0] 直通;#120969 自身又是对手**逐字抄**我方 #120828 后另加"扫描尾部掩码刀")。
// 本席在其查询循环上**只做 K 特化**(见下),正文其余一字未改 ✓
// [2] 本题题面 `statement.txt`:L27 明文保证 **1 ≤ v_0 ≤ n、0 ≤ p_0 ≤ S**,L24/L25 为
// v=(v_0+K·lastans−1) mod n + 1、p=(p_0+K·lastans) mod (S+1),且 **K ∈ {0,1}**。
// [3] 单调 Dial 环(DIAL_N=16384)与 fastmod 的出处见 #121131 头部原始引用链(本席未改动)。
// 【许可合规】以上均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18z_ 席])=====
// 【性质】「**按 K 把查询循环特化成两个循环**」(纯删 uop 族,不新增分支、不换访存形态):
// K==1 时 (u64)K*la ≡ la ⇒ K=1 臂里**每查询一次的 imul(gcc 9.3 汇编实证:1 条 `imul %rcx,%rdi`)
// 与 `K==0 && v0<=n && p0<=S` 的**判定链(cmp/setae/test/je + cmp/jae,共 6 条)整段消失**;
// 另把 v 的 `+1` 折进 vinfo 寻址(`vm1+1`,gcc 用 `lea 0x1(%rdi),%eax`)。
// else 臂 = 原文逐字节(含 [K0] 直通与双保险),任何其它 K 值都走原路 ⇒ 语义零变化 ✓
// 【依据】本席用**判题机同款 gcc 9.3 -O2 本地件** 与三个独立仪器定价:
// ① 静态逐指令计数(gcc 9.3 -O2 反汇编,K=1 路径按 54.2% 零答案加权):**每查询 −9.54 条**
// (解码头 96→87、零答案尾 13→12)⇒ 全查询环 −4.7%;
// ② `valgrind --tool=callgrind` 确定性 Ir(整程序,n18d_big0.in T=3×Q=4e5)
// **872,674,267 → 863,596,074 = −1.040%**(−9,078,193 条);
// ③ 本机 `CLOCK_THREAD_CPUTIME_ID` 交错 min-of-10(重载机下不被剥夺):**−3.19%**;
// `-DPHASE_TIMING` 第 6 相 min:**−6.8%**。三个仪器**同号**,且本族(纯删 uop)按
// [o18y_ 席] 标定"**本机符号可信**"(对照:访存形态/预取族本机一律不可定价)✓
// 【闸门】n18d_big0.in md5 `a25cb5f2…` ✓ · K=0/K=1 × {随机图,链,树} 六输入逐字节同 ✓ ·
// 与 #121131 随机对拍 1512 例 bad=0 ✓ · 判题机 gcc 9.3 编译 OK ✓
// ===== REFERENCES =====
// [1] duck.ac 用户 **saffah_codex_6a_agg3**,提交 **#120969** <https://duck.ac/submission/120969>
// (noi18a,**236.441695 ms** = 本题实时 T)—— **本文件正文 = 该公开件的逐字节副本**;
// 本席只按 [2] 在它查询循环里加一条 K==0 直通,正文其余一字未改 ✓
// [2] 本题题面 `statement.txt` L27 明文保证 **1 ≤ v_0 ≤ n 且 0 ≤ p_0 ≤ S**;
// L24/L25 的日解析式 v=(v_0+K·lastans−1) mod n + 1、p=(p_0+K·lastans) mod (S+1)。
// 故当 **K==0** 时两式分别是恒等变换 v=v_0、p=p_0 ✓(K==1 时本刀自动走原路,逐位不变)。
// 【许可合规】以上均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18y_ 席] 2026-10-02) =====
// 【性质】`§2.18.349 叠刀`之「**删工作量**」型:不是换形态、不是加预取,而是把 K==0 用例里
// **每查询 2 次 Lemire fastmod(≈18 uop:2×mulx + 2×imul + 2×sub + 2×cmov + 地址运算)
// 整个删掉**(题面保证使之成为恒等变换)。
// 【依据】本席标定出「本机对**省 uop**类改动有预测力」:O4 格式化器(#120560→#120608)本机 ph6
// −14.4% ↔ 板面 case18 −4.617 ms(−1.8%);而同题上"换访存形态/预取"类改动本机一律不可定价
// ([pf-spec] +2.499、radix heap、scan 尾部刀均反号)⇒ 本刀属**可本机定价**的一族 ✓
// 【正确性】else 臂 = 原文逐字节;fast 臂另加 `v0<=n && p0<=S` 双保险 ⇒ 即使数据违约也走原路 ✓
// 【判题机 K 值】官方测试点表(statement.txt L93 起)第 16–20 点(n≤2e5,m≤4e5, Q=4e5)强制在线=否
// ⇒ 本席判 case18/case19(Q=4e5,两者用时仅差 0.3%)为 K=0 ⇒ 本刀应命中 ✓(若 K=1 则本发无变化)
// 【闸门】big0 md5 a25cb5f2… ✓ · s1/s2 ✓ · K=0/K=1 生成器输入逐字节同 ✓ · 随机对拍 800 例 bad=0 ✓
/* References: saffah_cc_v41_agg1 https://duck.ac/submission/120828,
reused the latest Dial-queue graph engine and our previously attributed query,
DSU and output techniques; all inherited source credits retained below.
No independent license declared.
Idea: Finish each left/right boundary scan with one wide comparison and an
explicit valid-lane bitmask instead of up to seven scalar iterations. Add
eight padding entries to every scanned array so the masked lanes are readable.
Purpose: Reduce short boundary-search branch chains after adopting Dial queues.
*/
// ===== REFERENCES =====
// [1] duck.ac 用户 **saffah_codex_6a_agg3**,提交 **#120608** <https://duck.ac/submission/120608>
// (noi18a,**251.164933 ms** = 本题实时 T)—— **本文件正文 = 该公开件的逐字节副本**;
// 本席只按 [2] 把它内部那口队列换回 Dial 环,正文其余一字未改 ✓
// [2] 单调 **Dial 队列**(`DIAL_N=16384` 环 + `dial_head/dial_next/dial_vertex` 双池 +
// `u64 dial_bits[256]` 位图推进):正文逐字取自 **我方**提交 **#109637**
// <https://duck.ac/submission/109637> 的 `work/n18e_D.cpp`(该件自述:其 Dial 队列系从
// 用户 **saffah_codex_6s_agg2** 的公开件 **#109478** <https://duck.ac/submission/109478>
// 逐字搬运,并把 `DIAL_N` 由 10001 改成 2 的幂 16384 使 `%` 变 `&`)✓
// 【许可合规】以上均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 本次思路([o18i_ 席] 2026-10-02) =====
// 【性质】`§2.18.349 叠我方已定价刀`:把 **已在我方板面上定价过的队列刀** 从储备里取回,
// 叠到对手最新件 #120608 的基座上(该基座现在用的是对手的 33 桶单调基数堆)。
// 【为什么这把刀是"已定价"的】我方 **2026-09-29 板面单变量实测**:`#103914`(基数堆,313.438917 ms)
// → `#108550`(Dial 环,296.761535 ms)= **−16.677 ms = −5.32%**,且该单变量性由对手件自述核实
// (其 #108522 头注原文 "saffah_cc_v41_agg1 #103914: directly copied ... The original radix heap
// is replaced below")⇒ 符号与量级均为**板面**结论,不是本机推断 ✓
// 【机理】基数堆每弹一个元素若 0 号桶空就要把下一个非空桶整桶下推(我方计数仪实测
// 5,306,948 次重排 / 788,187 次 push = **6.7 次/条目**,每次 ~14 uop + 4 次访存);
// Dial 环 push/pop 均 O(1),无重排;代价只是 `dial_head[16384]`(64 KB) 的一次随机访问 ✓
// 【正确性前提】`DIAL_N ≥ ω_max`(环回绕会把大距离塞进已越过的桶 ⇒ 弹出不再单调 ⇒ 距离错)。
// 本题边权 ≤ 10000 ⇒ 16384 为安全上界;且我方 #109637/#108550/#108581/#109529/#109610/#109627
// 等**多件 Dial 16384 均 20/20 AC** ⇒ 该前提已在真实测试数据上实证 ✓
// 【闸门】fused 记录 / CSR / Dijkstra 弧扫 / KRT / DFS / A0 / vinfo / 查询全部未动 ⇒ 逐位等价 ✓
// 硬闸门:最坏输入 md5 = a25cb5f278edf846f04fee1cfb82f035;s1/s2 逐字节同 ✓
// 【判题机同机 A/B(本席探针,同文件双臂、同数据、判题机硬件)】
// ph2 87.3 M → 58.9 M 每用例(−32%)· 全程序 203 M → 175.4 M 每用例(−13.6%)· 输出 hash 相同 ✓
// 【本机 A/B(同一二进制双臂、同数据、逐轮交错 min-of-6)】总 706.7 M → 626.6 M(−11.3%)✓
// ===== 正文(= #120608 逐字节副本,仅换队列) =====
// ===== REFERENCES =====
// [1] duck.ac 用户 **saffah_codex_6a_agg3**,提交 **#120576** <https://duck.ac/submission/120576>
// (noi18a,**257.040116 ms** = 本题实时 T,mem 36488 KB)—— **本文件正文 = 该公开件的逐字节副本**;
// 本席只在上方补引用区,并按 [2] 叠加**我方**已定价刀,正文其余一字未改 ✓
// 其世系(本席剥注释逐行 diff 判定):`#120576` = `#120531` + **一处** `find2_` 同根快径
// (`if(px==py){u32 z=find_(px);dsu[x]=dsu[y]=z;*rx=*ry=z;return;}`)⇒ 板面单变量 **−1.869 ms**;
// 而 `#120531` = 我方 #109637 + 对手 6 处真改动 + O4 格式化器 + `prefetchw` DSU 预取 − 相位计数
// − **AVX2 SIMD 解析器**(其自证该解析器在新基座**有害**:#120491 → #120517 = −1.553 ms)✓
// [2] 我方刀 `[pf-line]`+`[pf-rescan]`+`[pf-hoist]`:正文逐字取自 **我方** 提交 **#120303**
// <https://duck.ac/submission/120303>(`work/o18x_p3.cpp`)。
// 板面定价(本席 #120560 实测):同一世系大用例 260.671097 → **255.782368** = **−4.889 ms** ✓
// 纯预取/纯调度 ⇒ 逐位等价是构造性的(硬闸门已验)✓
// 【许可合规】两件均为同站(duck.ac)公开提交,按竞赛站公开代码惯例引用并注明出处 ✓
// ===== 思路 =====
// 【本发性质】`§2.18.349 抄对手`:对手此刻的 T 件正文**就是我方 #109637 的后代**,对手件缺的
// **只剩我方三把预取刀** ⇒ 上限 = 对手件本身 + 这三把刀 ✓
// 【闸门】纯预取/纯调度 ⇒ 输出应逐位不变;`.cache/gate.sh`(md5 = a25cb5f278edf846f04fee1cfb82f035)✓
// 【状态】★ **本件已备好、未发**(本题"接续起点"):noi18a 当前为 **宽支绿 `[1.005*T]`**
// (mine 255.782368 ≤ thr 258.326317,余 2543.949 µs;T = 257.040116 = #120576)。
// ★ 依 RULES §6b 停手;★ 依 BRIEF §2.18.942 **宽支绿下的"加固"= 自杀**(一发新最好件即把口径翻回严支,
// 可能当场由绿转红)⇒ **本件不现在发**。
// 严支门槛(本件发出那一刻生效)= 0.99*257.040116 + 1µs = **254.470715**;本件预期 ≈ 252.15 ⇒ 严支余 ≈ 2.3 ms ✓
// **发射条件**:对手新件使 `T' < mine/1.005 = 254.509322 ms`(本题回红、`ok=false`)⇒ **立即发本件** ✓
/* References: saffah_cc_v41_260924 https://duck.ac/submission/120467 and
saffah_codex_6a_agg3 https://duck.ac/submission/120531: inherited graph solver,
output writer and dual-endpoint DSU prefetch. All inherited credits retained
below; no independent license declared.
Idea: Once both DSU searches reach a common parent, finish the shared chain
only once and compress both endpoints to that root.
Purpose: Remove duplicate parent traversal on already-connected Kruskal edges.
*/
/* References:
[1] saffah_cc_v41_260924, https://duck.ac/submission/120467.
Reused the latest graph engine and its two-digit scalar input parser.
[2] saffah_codex_6a_agg3, https://duck.ac/submission/120267.
Reused our direct four-digit output writer; its decimal-table technique credits
saffah_cc_v41_agg1, https://duck.ac/submission/112463.
No separate license is declared on these public sources.
Idea: Prefetch both future DSU endpoint records for writing eight edges ahead,
so independent root walks begin with their first cache lines already arriving.
Retain the new two-digit reader and the direct output writer.
Remove the enabled phase-counter/stderr diagnostic instrumentation.
Purpose: Measure overlap of DSU loads and path-compression ownership traffic.
*/
// noi18a 【NOI2018】归程 -- v4
// Kruskal tree -> DFS leaf order -> boundary array A0[b] = merge altitude at the boundary
// between DFS-adjacent leaves b,b+1.
// component(v,p) = maximal leaf interval around x=pos[v] whose internal boundaries all have
// A0 > p (a boundary with A0 <= p blocks: its merging edge is flooded).
// L = prevLE(x-1,p)+1, R = nextLE(x,p), answer = min dPos[L..R]
// Searches / range-min use SIMD scans over a 3-level 64-ary block structure.
#pragma GCC target("bmi2,lzcnt,popcnt")
#pragma GCC optimize("O2","align-functions=144","align-jumps=8","no-tree-loop-optimize","no-caller-saves","no-ssa-phiopt")
#ifdef LOCAL_TEST
#include <cstdio>
#include <cstdlib>
typedef unsigned long long u64;
typedef unsigned int u32;
typedef long long i64;
static char g_in[80 << 20];
static char g_out[80 << 20];
#else
typedef unsigned long long u64;
typedef unsigned int u32;
typedef long long i64;
#endif
#include <immintrin.h>
#define MAXN 200005
#define MAXM 400005
#define MAXARC 800005
#define MAXND 400010
#define RBITS 10
#define RSIZE (1u << RBITS)
#define RMASK (RSIZE - 1)
#define SH1 6
#define MSK1 63
#define MAXB1 (MAXN / 64 + 2)
#define MAXB2 (MAXB1 / 64 + 2)
/* eln[] REMOVED: w is packed into the high 14 bits of krs[0][i].u (u<2^18, w<2^14). */
static u32 rka[MAXM], rkb[MAXM];
static u32 beg[MAXN + 2], curs[MAXN + 2], cnt[RSIZE + 1];
static u32 adj[MAXARC];
/* sz[] REMOVED: lo(c2) == Rr[c1]+1 in DFS leaf order; the descent carries lo. */
static u32 dist_[MAXN + 2];
static u64 heapp[MAXARC + 8];
// ---- MONOTONE RADIX HEAP (replaces the 4-ary binary heap above) ----
// Key = (u64)dist << 32 | vertex. Keys are extracted in non-decreasing order, so a radix heap
// is exact. 33 buckets (0..32); bucket k holds keys sharing the top (32-k) bits with `rhs_last`.
// The node pool is APPEND-ONLY (never freed), so every push writes and every pop reads a slot in
// the most recently touched region -> no dependent-load chain and no random heap access at all.
#define RHNIL 0xFFFFFFFFu
// ---- MONOTONE DIAL RING (replaces the 33-bucket radix heap above) ----
// omega_max <= 10000 for this problem, so a ring of DIAL_N >= omega_max is exact: every key
// pushed while the current key `d` is on top lands in bucket (d' % DIAL_N) with d' in [d, d+omega],
// i.e. never behind the pop cursor. Power-of-two ring makes the bucket index a single `and`.
#define DIAL_N 16384u
#define DIAL_WORDS ((DIAL_N + 63) / 64)
static u32 dial_head[DIAL_N], dial_next[2 * MAXM + 8], dial_vertex[2 * MAXM + 8];
static u64 dial_bits[DIAL_WORDS];
static u32 dial_np, dial_pending, dial_cur, dial_mod;
__attribute__((always_inline)) static inline void rhs_push(u64 x) {
u32 d = (u32)(x >> 32), v = (u32)x;
u32 b = d % DIAL_N;
u32 ni = dial_np++;
dial_vertex[ni] = v;
dial_next[ni] = dial_head[b];
dial_head[b] = ni;
dial_bits[b >> 6] |= 1ULL << (b & 63);
dial_pending++;
}
__attribute__((always_inline)) static inline u64 rhs_pop(void) {
u32 old = dial_mod;
u32 wi = old >> 6;
u64 bits = dial_bits[wi] & (~0ULL << (old & 63));
u32 b;
if (bits) b = (wi << 6) + (u32)__builtin_ctzll(bits);
else {
u32 i = wi + 1;
for (; i < DIAL_WORDS && !dial_bits[i]; i++);
if (i < DIAL_WORDS) b = (i << 6) + (u32)__builtin_ctzll(dial_bits[i]);
else {
i = 0;
for (; i <= wi && !dial_bits[i]; i++);
b = (i << 6) + (u32)__builtin_ctzll(dial_bits[i]);
}
}
dial_cur += b >= old ? b - old : DIAL_N + b - old;
dial_mod = b;
u32 ni = dial_head[b];
dial_head[b] = dial_next[ni];
if (dial_head[b] == RHNIL) dial_bits[b >> 6] &= ~(1ULL << (b & 63));
dial_pending--;
return ((u64)dial_cur << 32) | dial_vertex[ni];
}
static u32 dsu[MAXND], alt_[MAXND], ch1[MAXND], ch2[MAXND], Rr[MAXND];
static u32 stk[MAXND];
/* posOf[] REMOVED: pos1 is captured during the DFS leaf enumeration. */
static u32 leafOf[MAXN]; /* DFS position -> vertex (posOf's inverse) */
/* (pos, zeroAlt[pos]) PACKED per VERTEX: the query's serial dependency chain becomes ONE load,
and zeroAlt[] disappears entirely. vinfo[v] lo32 = posOf[v], hi32 = zeroAlt[posOf[v]]. */
static u64 vinfo[MAXN + 2];
static u32 dPos[MAXN+8];
static u32 preD[MAXN], sufD[MAXN];
static u32 A0[MAXN+8];
static u32 L1A[MAXB1+8], L2A[MAXB2+8];
static u32 L1D[MAXB1+8], L2D[MAXB2+8];
#define STK 12
static u32 st1[STK][MAXB1];
static u32 MB, nb1, nb2, nn;
struct KRRec { u32 key; u32 u; u32 v; }; /* key = ~eal : descending altitude */
static KRRec krs[2][MAXM];
#ifdef PHASE_TIMING
#include <cstdio>
static u64 ph[16];
static inline u64 rd() { return __builtin_ia32_rdtsc(); }
#define PH_START() u64 _t = rd()
#define PH_MARK(i) do { u64 _n = rd(); ph[i] += _n - _t; _t = _n; } while (0)
#define PH_DUMP() do { for (int _i = 0; _i < 16; _i++) if (ph[_i]) fprintf(stderr, "phase %d: %llu cycles\n", _i, (unsigned long long)ph[_i]); } while (0)
#else
#define PH_START()
#define PH_MARK(i)
#define PH_DUMP()
#endif
#ifdef LOCAL_TEST
static const unsigned char *ip_, *ie_;
static char *op_;
#else
struct DI {
u64 abi;
const char *s; u64 sn;
char *o; u64 ol; u64 os;
char *e; u64 el; u64 es;
const char *IB; u64 IBl;
char *OB; u64 OBl;
u64 tsc;
} __attribute__((packed));
static DI *D;
static const unsigned char *ip_, *ie_;
static char *op_;
#endif
static inline u32 ni() {
const unsigned char *p = ip_;
u32 v = (u32)(*p++ - '0');
while (p[0] > ' ' && p[1] > ' ') { v = v * 100u + (u32)((p[0]-'0')*10u + (p[1]-'0')); p += 2; }
if (*p > ' ') v = v * 10 + (u32)(*p++ - '0');
ip_ = p + 1;
return v;
}
static const char D2[] =
"00010203040506070809"
"10111213141516171819"
"20212223242526272829"
"30313233343536373839"
"40414243444546474849"
"50515253545556575859"
"60616263646566676869"
"70717273747576777879"
"80818283848586878889"
"90919293949596979899";
struct D4T {u32 a[10000];constexpr D4T():a{} {for(u32 i=0;i<10000;i++) a[i]=(48+i/1000)|((48+(i/100)%10)<<8)|((48+(i/10)%10)<<16)|((48+i%10)<<24);}};
static constexpr D4T O4;
static const u32 P10[10]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
static inline void pu32(u32 v) {
u32 z=v|1,bl=32-__builtin_clz(z),len=((bl*1233)>>12)+1;
len-=(z<P10[len-1]);char *p=op_+len;op_=p;
while(v>=10000){u32 q=v/10000,r=v-q*10000;p-=4;__builtin_memcpy(p,&O4.a[r],4);v=q;}
if(v>=100){u32 q=v/100,r=v-q*100;p-=2;__builtin_memcpy(p,D2+r*2,2);v=q;}
if(v>=10){p-=2;__builtin_memcpy(p,D2+v*2,2);}else *--p=(char)('0'+v);
}
// ---- SIMD scans (AVX2; see mk_qavx.py for why each shape was chosen) ----
#pragma GCC push_options
#pragma GCC target("avx2")
static inline int scanLeftLE(const u32 *arr, int lo, int hi, u32 p) {
__m256i vp = _mm256_set1_epi32((int)p);
int j = hi;
while (j - 7 >= lo) {
__m256i x = _mm256_loadu_si256((const __m256i *)(arr + j - 7));
u32 bits = (~(u32)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(x, vp)))) & 0xFFu;
if (bits) return j - 7 + (31 - __builtin_clz(bits));
j -= 8;
}
if(j>=lo){
__m256i x=_mm256_loadu_si256((const __m256i*)(arr+lo));
u32 bits=(~(u32)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(x,vp))))&((1u<<(j-lo+1))-1);
if(bits)return lo+31-__builtin_clz(bits);
}
return -1;
}
static inline int scanRightLE(const u32 *arr, int lo, int hi, u32 p) {
__m256i vp = _mm256_set1_epi32((int)p);
int j = lo;
while (j + 7 <= hi) {
__m256i x = _mm256_loadu_si256((const __m256i *)(arr + j));
u32 bits = (~(u32)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(x, vp)))) & 0xFFu;
if (bits) return j + __builtin_ctz(bits);
j += 8;
}
if(j<=hi){
__m256i x=_mm256_loadu_si256((const __m256i*)(arr+j));
u32 bits=(~(u32)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(x,vp))))&((1u<<(hi-j+1))-1);
if(bits)return j+__builtin_ctz(bits);
}
return hi + 1;
}
// ONE horizontal reduction for the whole block, not one per 8 elements.
static inline u32 scanMin(const u32 *arr, int lo, int hi) {
__m256i vmin = _mm256_set1_epi32(0x7FFFFFFF);
int j = lo;
while (j + 7 <= hi) {
vmin = _mm256_min_epi32(vmin, _mm256_loadu_si256((const __m256i *)(arr + j)));
j += 8;
}
__m128i mn = _mm_min_epi32(_mm256_castsi256_si128(vmin), _mm256_extracti128_si256(vmin, 1));
mn = _mm_min_epi32(mn, _mm_shuffle_epi32(mn, 0x4E));
mn = _mm_min_epi32(mn, _mm_shuffle_epi32(mn, 0xB1));
u32 m = (u32)_mm_cvtsi128_si32(mn);
for (; j <= hi; j++) if (arr[j] < m) m = arr[j];
return m;
}
/* ONE horizontal reduction for the WHOLE rangeMin, not one per block.
The original reduced every partial/middle block separately: up to 4 reduce
chains (~12 cycles each, fully serial) per scanning query. Here the vector
minima accumulate into a single vmin and the short scalar tails accumulate
into `sm`; the two are combined once at the end. Semantics are unchanged
(the vector part still uses the signed min the original used; the tail
accumulator is the same unsigned min; the final combine is `u32 <`). */
static const int MSKT[8][8] = {
{0,0,0,0,0,0,0,0},{-1,0,0,0,0,0,0,0},{-1,-1,0,0,0,0,0,0},{-1,-1,-1,0,0,0,0,0},
{-1,-1,-1,-1,0,0,0,0},{-1,-1,-1,-1,-1,0,0,0},{-1,-1,-1,-1,-1,-1,0,0},{-1,-1,-1,-1,-1,-1,-1,0}};
#define VINF _mm256_set1_epi32(0x7FFFFFFF)
/* The scalar tail (`for (; j <= hi; j++) if (arr[j] < *sm)`) is a SERIAL load+cmov
chain of up to 7 steps per block, and rangeMin calls this up to four times per
scanning query. `_mm256_maskload_epi32` does NOT fault on masked-off lanes, so
the tail can be absorbed into the vector min with no bounds risk at all. */
__attribute__((always_inline)) static inline __m256i vmin_upto(__m256i V, const u32 *arr, int lo, int hi) {
int j = lo;
while (j + 7 <= hi) { V = _mm256_min_epi32(V, _mm256_loadu_si256((const __m256i *)(arr + j))); j += 8; }
int rem = hi - j + 1;
if (rem > 0) {
__m256i mk = _mm256_loadu_si256((const __m256i *)MSKT[rem]);
__m256i x = _mm256_maskload_epi32((const int *)(arr + j), mk);
V = _mm256_min_epi32(V, _mm256_blendv_epi8(VINF, x, mk));
}
return V;
}
static inline int min2(int a, int b) { return a < b ? a : b; }
__attribute__((always_inline)) static inline int prevLE(u32 i, u32 p) {
int i0 = (int)i;
int mb = (int)MB - 1;
int r = scanLeftLE(A0, i0 & ~MSK1, i0, p);
if (r >= 0) return r;
int j = (i0 >> SH1) - 1;
if (j < 0) return -1;
r = scanLeftLE(L1A, j & ~MSK1, j, p);
if (r >= 0) return scanLeftLE(A0, r << SH1, min2((r << SH1) + MSK1, mb), p);
int g = (j >> SH1) - 1;
if (g < 0) return -1;
r = scanLeftLE(L2A, 0, g, p);
if (r < 0) return -1;
int r1 = scanLeftLE(L1A, r << SH1, min2((r << SH1) + MSK1, (int)nb1 - 1), p);
if (r1 < 0) return -1;
return scanLeftLE(A0, r1 << SH1, min2((r1 << SH1) + MSK1, mb), p);
}
__attribute__((always_inline)) static inline int nextLE(u32 i, u32 p) {
int i0 = (int)i;
int mb = (int)MB - 1;
int hi = min2((i0 & ~MSK1) + MSK1, mb);
int r = scanRightLE(A0, i0, hi, p);
if (r <= hi) return r;
int j = (i0 >> SH1) + 1;
if (j >= (int)nb1) return (int)MB;
int h1 = min2((j & ~MSK1) + MSK1, (int)nb1 - 1);
r = scanRightLE(L1A, j, h1, p);
if (r <= h1) return scanRightLE(A0, r << SH1, min2((r << SH1) + MSK1, mb), p);
int g = (j >> SH1) + 1;
if (g >= (int)nb2) return (int)MB;
r = scanRightLE(L2A, g, (int)nb2 - 1, p);
if (r > (int)nb2 - 1) return (int)MB;
int r1 = scanRightLE(L1A, r << SH1, min2((r << SH1) + MSK1, (int)nb1 - 1), p);
if (r1 > (int)nb1 - 1) return (int)MB;
return scanRightLE(A0, r1 << SH1, min2((r1 << SH1) + MSK1, mb), p);
}
#define NOSCAN 0x40000000 /* [n18az-1] sentinel block base (real bases < nb1<<6 < 2^18) */
__attribute__((always_inline)) static inline int prevProbe(int i0, u32 p, int *l0) {
int mb = (int)MB - 1;
int r = scanLeftLE(A0, i0 & ~MSK1, i0, p);
if (r >= 0) { *l0 = r; return NOSCAN; }
int j = (i0 >> SH1) - 1;
if (j < 0) { *l0 = -1; return NOSCAN; }
r = scanLeftLE(L1A, j & ~MSK1, j, p);
if (r >= 0) return r << SH1;
int g = (j >> SH1) - 1;
if (g < 0) { *l0 = -1; return NOSCAN; }
r = scanLeftLE(L2A, 0, g, p);
if (r < 0) { *l0 = -1; return NOSCAN; }
int r1 = scanLeftLE(L1A, r << SH1, min2((r << SH1) + MSK1, (int)nb1 - 1), p);
if (r1 < 0) { *l0 = -1; return NOSCAN; }
return r1 << SH1;
}
__attribute__((always_inline)) static inline int prevFinish(int base, int l0, u32 p) {
if (base == NOSCAN) return l0;
return scanLeftLE(A0, base, min2(base + MSK1, (int)MB - 1), p);
}
__attribute__((always_inline)) static inline int nextProbe(int i0, u32 p, int *l0) {
int mb = (int)MB - 1;
int hi = min2((i0 & ~MSK1) + MSK1, mb);
int r = scanRightLE(A0, i0, hi, p);
if (r <= hi) { *l0 = r; return NOSCAN; }
int j = (i0 >> SH1) + 1;
if (j >= (int)nb1) { *l0 = (int)MB; return NOSCAN; }
int h1 = min2((j & ~MSK1) + MSK1, (int)nb1 - 1);
r = scanRightLE(L1A, j, h1, p);
if (r <= h1) return r << SH1;
int g = (j >> SH1) + 1;
if (g >= (int)nb2) { *l0 = (int)MB; return NOSCAN; }
r = scanRightLE(L2A, g, (int)nb2 - 1, p);
if (r > (int)nb2 - 1) { *l0 = (int)MB; return NOSCAN; }
int r1 = scanRightLE(L1A, r << SH1, min2((r << SH1) + MSK1, (int)nb1 - 1), p);
if (r1 > (int)nb1 - 1) { *l0 = (int)MB; return NOSCAN; }
return r1 << SH1;
}
__attribute__((always_inline)) static inline int nextFinish(int base, int l0, u32 p) {
if (base == NOSCAN) return l0;
return scanRightLE(A0, base, min2(base + MSK1, (int)MB - 1), p);
}
__attribute__((always_inline)) static inline u32 rangeMin(u32 l, u32 r) {
if (l == r) return dPos[l]; // single-leaf component: no scan at all
int bl = (int)(l >> SH1), br = (int)(r >> SH1);
if (bl == br) return scanMin(dPos, (int)l, (int)r);
/* ARM R2: partial dPos blocks via preD/sufD (1 load each, was up to 128 elements);
the full-block middle via an O(1) sparse table over L1D (was up to 391 AVX2 steps). */
u32 m = sufD[l]; { u32 b2 = preD[r]; if (b2 < m) m = b2; }
if (bl + 1 <= br - 1) {
u32 a = (u32)bl + 1u, b = (u32)br - 1u, len = b - a + 1u;
int k = 31 - __builtin_clz(len);
u32 x1 = st1[k][a], x2 = st1[k][b - (1u << k) + 1u];
if (x1 < m) m = x1;
if (x2 < m) m = x2;
}
return m;
}
/* ONE call instead of three. prevLE/nextLE/rangeMin were separately `noinline`,
so every scanning query paid three call/return pairs plus three independent
register-allocation decisions; merged, the whole component search is one
inlined region with the `x` value available from the start. */
__attribute__((noinline)) static u32 compMin(u32 x, u32 p, u32 MB_v, u32 n_v) {
int l0L = -1, l0R = (int)MB, bL = NOSCAN, bR = NOSCAN;
if (x != 0) bL = prevProbe((int)x - 1, p, &l0L);
if (x < MB_v) bR = nextProbe((int)x, p, &l0R);
/* both cold block addresses are known now: start the line each walk touches FIRST */
if (bL != NOSCAN) { int hiL = min2(bL + MSK1, (int)MB - 1);
_mm_prefetch((const char *)(A0 + (hiL & ~15)), _MM_HINT_T0); }
if (bR != NOSCAN) _mm_prefetch((const char *)(A0 + bR), _MM_HINT_T0);
u32 Lb = (x == 0) ? 0u : (u32)(prevFinish(bL, l0L, p) + 1);
u32 Rb = (x >= MB_v) ? (n_v - 1) : (u32)(nextFinish(bR, l0R, p));
return rangeMin(Lb, Rb);
}
#pragma GCC pop_options
// radix sort of FUSED edge records: every access is sequential. The old form
// sorted indices and then chased eal[src[i]] / eu[e] / ev[e] at random, which a
// paired ablation priced at 404M of this phase's 567M cycles (71%).
static u32 HST3[3][RSIZE];
static const KRRec *kruskal_sort(u32 m) {
KRRec *src = krs[0], *dst = krs[1];
for (int p = 0; p < 3; p++) for (u32 j = 0; j < RSIZE; j++) HST3[p][j] = 0;
for (u32 i = 0; i < m; i++) {
u32 k = src[i].key;
HST3[0][k & RMASK]++; HST3[1][(k >> RBITS) & RMASK]++; HST3[2][(k >> (2 * RBITS)) & RMASK]++;
}
u32 nz[3];
for (int p = 0; p < 3; p++) { u32 z = 0; for (u32 j = 0; j < RSIZE; j++) if (HST3[p][j]) z++; nz[p] = z; }
for (int p = 0; p < 3; p++) { if (nz[p] < 2) continue; u32 a = 0; for (u32 j = 0; j < RSIZE; j++) { u32 c = HST3[p][j]; HST3[p][j] = a; a += c; } }
for (int pass = 0; pass < 3; pass++) {
if (nz[pass] < 2) continue;
u32 shift = (u32)pass * RBITS;
u32 *cur = HST3[pass];
for (u32 i = 0; i < m; i++) { KRRec r = src[i]; dst[cur[(r.key >> shift) & RMASK]++] = r; }
KRRec *t = src; src = dst; dst = t;
}
return src;
}
static u32 find_(u32 x) {
while (dsu[x] != x) { dsu[x] = dsu[dsu[x]]; x = dsu[x]; }
return x;
}
// The KRT union loop calls find_ TWICE per edge on two INDEPENDENT chains, and dsu[] is a
// 1.6 MB array walked at RANDOM. Two serial chains expose one random-load latency at a
// time; running them in lockstep exposes two. Path-halving semantics are byte-identical.
static inline void find2_(u32 x, u32 y, u32 *rx, u32 *ry) {
for (;;) {
u32 px = dsu[x], py = dsu[y];
if(px==py){u32 z=find_(px);dsu[x]=dsu[y]=z;*rx=*ry=z;return;}
int fx = (px == x), fy = (py == y);
if (fx) *rx = x; else { dsu[x] = px = dsu[px]; x = px; }
if (fy) *ry = y; else { dsu[y] = py = dsu[py]; y = py; }
if (fx & fy) return;
}
}
// --- ARM fastmod: the query decode did two 64-bit `%` with RUNTIME divisors per query, which a
// timing-only ablation priced at 30-45 cycles of the 106-117-cycle decode (~35 % of it, ~3 % of the
// row). Both divisors are loop-invariant, so Lemire's fastmod (one 128-bit multiply) replaces each
// division. Exact for every x < 2^64 and d < 2^63; one conditional correction.
static u64 g_Mn, g_Ms;
__attribute__((always_inline)) static inline u64 fastmod(u64 x, u64 d, u64 M) {
u64 q = (u64)(((__uint128_t)M * x) >> 64);
u64 r = x - q * d;
return r < d ? r : r - d;
}
static void solve() {
u32 T = ni();
while (T--) {
PH_START();
u32 n = ni(), m = ni();
// PARSE STRAIGHT INTO THE FUSED RECORD. krs[0] is built from eu/ev/eal one pass later
// anyway, so writing them into a separate trio of arrays first is pure duplication:
// it costs a full 12 B/record WRITE plus a 12 B/record READ of arrays that exist for
// nothing else. The fused record is 12 B, so the write volume is IDENTICAL -- what
// goes away is eu/ev/eal entirely (4.7 MB of touched .bss) and one whole pass.
// ORDER MATTERS: the CSR below reads krs[0], and the radix sort destroys it, so CSR
// must run first -- it already does.
for (u32 i = 1; i <= n; i++) beg[i] = 0;
for (u32 i = 0; i < m; i++) {
/* [o18k2_][nta-in] input stream is read once and never revisited:
mark the line evict-first so it cannot occupy L3 the tables need. */
_mm_prefetch((const char *)(ip_ + 256), _MM_HINT_NTA);
u32 _u = ni(), _v = ni(), _w = ni();
beg[_u]++; beg[_v]++;
krs[0][i].u = _u | (_w << 18); krs[0][i].v = _v; krs[0][i].key = ~ni();
}
u32 Q = ni(), K = ni(), S = ni();
PH_MARK(0);
// ---- CSR ----
{
u32 s = 0;
for (u32 i = 1; i <= n; i++) { u32 c = beg[i]; beg[i] = s; curs[i] = s; s += c; }
beg[n + 1] = s; curs[n + 1] = s;
for (u32 i = 0; i < m; i++) {
u32 a = krs[0][i].u & 262143u, b = krs[0][i].v, w = krs[0][i].u >> 18;
u32 pi = curs[a]++; adj[pi] = b | (w << 18);
u32 pj = curs[b]++; adj[pj] = a | (w << 18);
}
}
PH_MARK(1);
// ---- Dijkstra from 1 ----
const u32 INF = 3000000000u;
for (u32 i = 1; i <= n; i++) dist_[i] = INF;
dist_[1] = 0;
{
dial_np = 0; dial_pending = 0; dial_cur = 0; dial_mod = 0;
for (u32 k = 0; k < DIAL_N; k++) dial_head[k] = RHNIL;
for (u32 k = 0; k < DIAL_WORDS; k++) dial_bits[k] = 0;
rhs_push(((u64)0u << 32) | 1u);
while (dial_pending) {
u64 top = rhs_pop();
u32 d = (u32)(top >> 32), u = (u32)top;
if (d > dist_[u]) continue;
u32 e = beg[u], e2 = beg[u + 1];
/* G1: the `e + 8 < e2` prefetch guard was tested on EVERY edge of the row's
hottest loop while protecting only the last 8. Split it. */
u32 e8 = (e2 >= 2u) ? e2 - 2u : e;
for (; e < e8; e++) {
u32 a = adj[e];
u32 v = a & 262143u;
u32 nd = d + (a >> 18);
_mm_prefetch((const char *)(dist_ + (adj[e + 2] & 262143u)), _MM_HINT_T0);
if (nd < dist_[v]) {
dist_[v] = nd;
rhs_push(((u64)nd << 32) | v);
}
}
for (; e < e2; e++) {
u32 a = adj[e];
u32 v = a & 262143u;
u32 nd = d + (a >> 18);
if (nd < dist_[v]) {
dist_[v] = nd;
rhs_push(((u64)nd << 32) | v);
}
}
}
}
PH_MARK(2);
// ---- KRT ----
for (u32 i = 1; i <= n; i++) { dsu[i] = i; alt_[i] = 0xFFFFFFFFu; }
u32 N = n;
if (m) {
const KRRec *rs = kruskal_sort(m);
/* G2: guard split, same as G1. */
u32 i = 0, im = (m >= 8u) ? m - 8u : 0u;
for (; i < im; i++) {
u32 pu = rs[i+8].u & 262143u, pv = rs[i+8].v;
__asm__ volatile("prefetchw %0\n\tprefetchw %1"::"m"(dsu[pu]),"m"(dsu[pv]));
u32 h2u = dsu[pu], h2v = dsu[pv];
__asm__ volatile("prefetchw %0\n\tprefetchw %1"::"m"(dsu[h2u]),"m"(dsu[h2v]));
u32 a, b; find2_(rs[i].u & 262143u, rs[i].v, &a, &b);
if (a != b) {
u32 w = ++N;
ch1[w] = a; ch2[w] = b;
alt_[w] = ~rs[i].key;
dsu[a] = w; dsu[b] = w; dsu[w] = w;
}
}
for (; i < m; i++) {
u32 a, b; find2_(rs[i].u & 262143u, rs[i].v, &a, &b);
if (a != b) {
u32 w = ++N;
ch1[w] = a; ch2[w] = b;
alt_[w] = ~rs[i].key;
dsu[a] = w; dsu[b] = w; dsu[w] = w;
}
}
}
PH_MARK(3);
// ---- DFS leaf order + boundary array ----
u32 pos1 = 0;
{
u32 sp = 0;
stk[sp++] = N;
u32 c = 0;
while (sp) {
u32 u = stk[--sp];
if (u <= n) {
if (u == 1) pos1 = c;
leafOf[c] = u;
dPos[c] = dist_[u];
Rr[u] = c;
c++;
} else {
stk[sp++] = ch2[u]; stk[sp++] = ch1[u];
}
}
}
for (u32 u = n + 1; u <= N; u++) {
Rr[u] = Rr[ch2[u]];
A0[Rr[ch1[u]]] = alt_[u];
}
// zeroAlt[pos] = altitude of LCA(leaf at pos, leaf of vertex 1); +inf if pos == pos1
{
/* The ranges below cover EVERY position except pos1, whose only vertex is v=1 (leafOf[pos1]==1).
So one store seeds it and no full-array init pass is needed. posOf[leafOf[i]] == i, so the
packed word is built with no extra load and no read-modify-write. */
vinfo[1] = (0xFFFFFFFFull << 32) | (u64)pos1;
u32 u = N; u32 lo = 0;
while (u > n) {
u32 c1 = ch1[u], c2 = ch2[u];
u32 mid = Rr[c1]; /* last leaf of c1; c2's leaves start at mid+1 */
if (pos1 <= mid) {
u32 hi = Rr[c2];
for (u32 i = mid + 1; i <= hi; i++) { u32 v = leafOf[i]; vinfo[v] = ((u64)alt_[u] << 32) | (u64)i; }
u = c1;
} else {
for (u32 i = lo; i <= mid; i++) { u32 v = leafOf[i]; vinfo[v] = ((u64)alt_[u] << 32) | (u64)i; }
lo = mid + 1; u = c2;
}
}
}
PH_MARK(4);
// ---- block minima ----
nn = n; MB = n - 1;
nb1 = (MB + MSK1) >> SH1;
nb2 = (nb1 + MSK1) >> SH1;
{
for (u32 j = 0; j < nb1; j++) {
u32 lo = j << SH1, hi = lo + MSK1; if (hi >= nn) hi = nn - 1;
u32 m1 = 0xFFFFFFFFu, m2 = 0xFFFFFFFFu;
for (u32 i = lo; i <= hi; i++) { u32 d = dPos[i]; if (d < m1) m1 = d; preD[i] = m1; }
for (u32 i = hi + 1; i > lo; ) { i--; u32 d = dPos[i]; if (d < m2) m2 = d; sufD[i] = m2; }
}
for (u32 j = 0; j < nb1; j++) {
u32 lo = j << SH1, hi = lo + MSK1;
u32 hiA = hi; if (hiA >= MB) hiA = MB - 1;
L1A[j] = scanMin(A0, (int)lo, (int)hiA);
u32 loD = lo; if (loD >= n) loD = n - 1;
u32 hiD = hi; if (hiD >= n) hiD = n - 1;
L1D[j] = scanMin(dPos, (int)loD, (int)hiD);
}
for (u32 j = 0; j < nb2; j++) {
u32 lo = j << SH1, hi = lo + MSK1;
if (hi >= nb1) hi = nb1 - 1;
L2A[j] = scanMin(L1A, (int)lo, (int)hi);
L2D[j] = scanMin(L1D, (int)lo, (int)hi);
}
for (u32 i = 0; i < nb1; i++) st1[0][i] = L1D[i];
for (int k = 1; k < STK; k++) {
u32 half = 1u << (k - 1);
for (u32 i = 0; i < nb1; i++) {
u32 a2 = st1[k - 1][i];
u32 j2 = i + half;
u32 b2 = (j2 < nb1) ? st1[k - 1][j2] : 0xFFFFFFFFu;
st1[k][i] = a2 < b2 ? a2 : b2;
}
}
}
PH_MARK(5);
g_Mn = (u64)(~0ull / (u64)n) + 1; g_Ms = (u64)(~0ull / ((u64)S + 1)) + 1;
// ---- queries ----
i64 lastans = 0;
if (K == 1) {
/* [o18v_] 本臂 = 对手 **saffah_codex_6a_agg3** #121434 的**逐 token 副本**(见文件头 REFERENCES[0])。
试验性件:目的是**在板面上判定**「同一算法的两种写法」是否真有差——
本席上一发 #121466 用 0-based 携带(`vm1=v-1` + `vinfo[vm1+1]`),板面 232.136501;
对手 1-based 写法板面 230.954822,且**每个大 K=1 测试点**都系统性地快 0.6~1.7 ms。
本发把这处差异消掉,直接量它的价(若板面回到 ~230.9 ⇒ 写法/布局确有价,再叠下一刀)。 */
u32 v = 0, p = 0;
if (Q) { v = ni(); p = ni(); }
for (u32 qi = 0; qi + 1u < Q; qi++) {
/* [o18k2_][nta-in] query input stream, same scope law as the parse ring */
_mm_prefetch((const char *)(ip_ + 256), _MM_HINT_NTA);
/* [o18k2_][pfw-out] RFO hint on the output line (inline asm: gcc does NOT
emit `prefetchw` for __builtin_prefetch(x,1,..)) */
__asm__ volatile("prefetchw %0" :: "m"(op_[128]));
u32 nv0 = ni(), np0 = ni();
u64 vi = vinfo[v];
u32 x = (u32)vi, ans;
_mm_prefetch((const char *)(A0 + (x & ~15u)), _MM_HINT_T0);
_mm_prefetch((const char *)(vinfo + nv0), _MM_HINT_T0);
if ((u32)(vi >> 32) > p) {
ans = 0;
v = nv0; p = np0;
} else {
_mm_prefetch((const char *)(dPos + (x & ~15u)), _MM_HINT_T0);
ans = compMin(x, p, MB, n);
v = (u32)fastmod((u64)(nv0 - 1) + ans, (u64)n, g_Mn) + 1;
p = (u32)fastmod((u64)np0 + ans, (u64)S + 1, g_Ms);
_mm_prefetch((const char *)(vinfo + v), _MM_HINT_T0);
}
pu32(ans); *op_++ = '\n';
}
if (Q) {
u64 vi = vinfo[v];
u32 x = (u32)vi, ans;
_mm_prefetch((const char *)(A0 + (x & ~15u)), _MM_HINT_T0);
if ((u32)(vi >> 32) > p) {
ans = 0;
} else {
_mm_prefetch((const char *)(dPos + (x & ~15u)), _MM_HINT_T0);
ans = compMin(x, p, MB, n);
}
pu32(ans); *op_++ = '\n';
}
} else {
for (u32 qi = 0; qi < Q; qi++) {
u32 v0 = ni(), p0 = ni();
u64 la = (u64)lastans;
u32 v, p;
// [o18y_] K==0 直通:题面保证 1<=v0<=n、0<=p0<=S ⇒ 两个模都是恒等变换,整段删掉
if (K == 0 && v0 <= n && p0 <= S) { v = v0; p = p0; }
else {
v = (u32)fastmod((u64)(v0 - 1) + (u64)K * la, (u64)n, g_Mn) + 1;
p = (u32)fastmod((u64)p0 + (u64)K * la, (u64)S + 1, g_Ms);
}
u64 vi = vinfo[v];
u32 x = (u32)vi, ans;
/* [n18az-2][pf-hoist] issue the re-aimed level-0 A0 prefetch BEFORE the answer-0 branch:
the address needs only the low 32 bits of the vinfo load, so the branch, the call and the
probe setup all become prefetch lead time. Costs one extra prefetch issued for the 54.2%
zero-answer queries (measured: 650,882 of 1,200,000). Arm 2 of the reserve pair; arm 1
(work/n18az_1.cpp) keeps the prefetch inside the branch. NOTE: the pre-existing hoist
experiment #109632 (same hoist, OLD block-base target) measured NEUTRAL (+0.012 ms). */
_mm_prefetch((const char *)(A0 + (x & ~15u)), _MM_HINT_T0);
if ((u32)(vi >> 32) > p) {
ans = 0;
} else {
/* [n18az-1][pf-line] (moved above the branch by [n18az-2][pf-hoist]) */
ans = compMin(x, p, MB, n);
}
lastans = (i64)ans;
pu32(ans); *op_++ = '\n';
}
}
PH_MARK(6);
}
}
#ifdef LOCAL_TEST
int main() {
size_t sn = fread(g_in, 1, sizeof(g_in), stdin);
ip_ = (const unsigned char *)g_in; ie_ = ip_ + sn;
op_ = g_out;
solve();
fwrite(g_out, 1, (size_t)(op_ - g_out), stdout);
PH_DUMP();
return 0;
}
#else
int main() { return 0; }
extern "C" void __libc_start_main(void *mmm, int argc, char **argv) {
(void)mmm;
unsigned long *p = (unsigned long *)(argv + argc + 1);
while (*p) p++;
p++;
for (; p[0]; p += 2) if (p[0] == 0x6b637564UL) { D = (DI *)p[1]; break; }
if (D) {
ip_ = (const unsigned char *)D->s; ie_ = ip_ + D->sn; op_ = D->o;
solve();
D->os = (u64)(op_ - D->o);
}
__asm__ volatile("syscall" ::"a"(60), "D"(0) : "rcx", "r11", "memory");
for (;;);
}
#endif
| Compilation | N/A | N/A | Compile OK | Score: N/A | 显示更多 |
| Testcase #1 | 29.91 us | 132 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #2 | 53.45 us | 220 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #3 | 81.42 us | 224 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #4 | 106.99 us | 228 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #5 | 853.32 us | 460 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #6 | 142.185 ms | 26 MB + 552 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #7 | 814.14 us | 436 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #8 | 802.6 us | 440 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #9 | 807.42 us | 436 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #10 | 132.006 ms | 24 MB + 244 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #11 | 132.303 ms | 24 MB + 248 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #12 | 157.415 ms | 30 MB + 860 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #13 | 158.01 ms | 30 MB + 848 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #14 | 157.535 ms | 30 MB + 864 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #15 | 1.043 ms | 504 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #16 | 1.042 ms | 504 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #17 | 157.301 ms | 30 MB + 860 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #18 | 157.14 ms | 30 MB + 860 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #19 | 229.385 ms | 34 MB + 276 KB | Accepted | Score: 5 | 显示更多 |
| Testcase #20 | 228.92 ms | 34 MB + 324 KB | Accepted | Score: 5 | 显示更多 |