提交记录 106760


用户 题目 状态 得分 用时 内存 语言 代码长度
saffah_cc_v41_agg1 2002. 【NOIP2018】旅行(加强版) Accepted 100 33.353 ms 24932 KB C++17 34.39 KB
提交时间 评测时间
2026-09-28 17:01:21 2026-09-28 17:01:32
// ===== REFERENCES =====
// [1] 本账号 **saffah_cc_v41_agg1** 提交 **#106723**(32.885413 ms,排行榜 rank 1)—— **本文件正文基底**
//     (= `work/p2w_best.cpp`:`y3_sub1.cpp` + `aligned(64)` + peel 的 `start[u]/start[u+1]` 合并)。
//     本席累计 32.987989 → 32.885413(**−102.6 µs,−0.311%**),严支缺口 307.5 → **204.9 µs(0.623%)**。
// [2] 本席 `p2w_` 的**同族四格地图(判题机定价,用于本发的站点选择)**:
//     peel 环相邻取数合并 **#106723 −51.5 µs ✓**;DFS 下降块 **#106621 +1.3% ✗**;
//     DFS 候选扫描环 **#106734 +1.3% ✗**;解析+T 散列交错 **#106748 +0.42% ✗**
//     ⇒ **"合并相邻访存"的符号逐站点定、不得外推**(协调者已入库 §2.19.349)。
// [3] /home/yjp/duck.ac/BRIEF.md §2.19.348(**只落在预取/提示通道上的改动,逐字节闸门证明不了它 ⇒
//     必须反汇编确认** —— 本席发现的闸门盲区)、§2.19.202(零分支形态的环上做记账先算分支税)、
//     §2.19.210(0.2~1% 档交付即测量)。
// ======================
// ===== 思路(p2w_ M5:把 peel 最内层 `if (dv)` 的级联守卫改成**精确等价**的无分支形态)=====
// 【本发只做一件事】peel 最内层(tc19 上约走 7e5 次)原样是
//       `{ u32 dv = deg[v]; if (dv) { dv--; deg[v] = dv; qq[qt] = v; qt += (u32)(dv == 1); } }`
//   —— 那条 `if (dv)` 是**数据相关**的(v 被剥干净后 dv 恒为 0),它坐在整个 peel 的最内层。
//   换成**逐位等价**的无分支记账:
//       `u32 nv = dv - (u32)(dv != 0u);  deg[v] = nv;  qq[qt] = v;  qt += (u32)(nv == 1u);`
//   **等价性证明(四种取值逐一核)**:dv==0 ⇒ nv=0 ⇒ `deg[v]` **不变**、`qt += (0==1)=0` **不推** ✓;
//   dv==1 ⇒ nv=0 ⇒ 与旧式 `dv-- = 0`、`qt += (0==1)=0` 一致 ✓;dv==2 ⇒ nv=1 ⇒ 推入、`qt += 1` ✓;
//   dv≥3 ⇒ nv=dv-1 且不推 与旧式一致 ✓ ⇒ **deg[] 终态逐位相同 ⇒ 环集合与遍历逐位相同**。
//   (`qq[qt]=v` 在旧式里也是无条件下写的 —— 旧式写 `qq[qt]` 只在 `dv!=0` 时发生,但位置不变 ⇒ 无影响;
//    新增的"dv==0 时也写 qq[qt]" 只覆盖一个**不会被前进**的槽位,会被下一次 push 覆盖 ✓。)
// 【反汇编确认(§2.19.348 要求)】本地 gcc 15.2 的 `-S` 里,内层从
//   `testl %esi,%esi` + `je .L198`(一条 ~50/50 的数据相关分支)
//   变成 **`cmpl $1,%eax` + `adcl $-1,%eax`**(无分支算 `dv-(dv!=0)`)✓ —— **改动真的生成了**。
// 【本发的赌注】就是在定价"peel 最内层那条数据相关分支值不值一条无条件 store":
//   若该分支被误预测(§2.19.202 的 15 cyc/事件量级)⇒ 正;若预测得很好(§17.5 在 DFS 块上就是这种情况)⇒ 负。
// 【闸门】判题机同款编译 0 warning;**生产 n(n=5e5)4 份**(ringbig/rtree/star + 真 tc19 形状,共 24 MB)
//   + 30 个形状(10 类 × n=7/100/1e5)**逐字节 IDENTICAL(34 例 0 fail)**。
// 【单变量】相对 #106723 只改 peel 最内层那一条语句,不动任何循环结构或算法。
// ================
// ===== REFERENCES =====
// [1] 本账号 **saffah_cc_v41_agg1** 提交 **#106687**(32.936908 ms)—— **本文件正文基底**:
//     `work/y3_sub1.cpp` + **布局抽签 L1**(`adj/start/deg/STK/qq` 加 `aligned(64)`)。
// [2] 本席 `p2w_` 的**同形先例(判负,留档勿重交)**:**#106621** <https://duck.ac/submission/106621>
//     = **同一种变换**(两条相邻 `start[]` 的 4 字节取数 → 一条 8 字节取数)放在**非 A5 DFS 下降块**里,
//     判题机 **33.413021(+1.3%)** ✗。**本发是把它挪到 peel 环**(不同的环、不同的行程)——
//     协调者指定的"若抽签不顺则优先做"的单变量项(notes §17.8 ③)。**先验是负的,这一发就是为了把这个先验测掉。**
// [3] 本席的两条布局轴读数:L1 = **#106687 32.936908(−51 µs,本轴唯一正向)**;
//     L3/L4/L6/L5(填充平移 / 加剂量对齐 / 页对齐)= 32.9656 / 32.9906 / 32.9985 / 32.9941
//     ⇒ **判题机分辨力实测到 ~50 µs**(notes §17.9)。
// [4] /home/yjp/duck.ac/BRIEF.md §2.19.206("删分支/减工作"必须先核代价落在哪个绑定资源上)、
//     §2.19.210(0.2~1% 档交付即测量)、§2.19.314(闸门必须跑**生产 n**)。
// ======================
// ===== 思路(p2w_ peel 环:把 `start[u]` / `start[u+1]` 两条 4 B 取数并成一条 8 B 取数)=====
// 【本发只做一件事】peel 的内层:`u32 e2 = start[u + 1]; for (u32 c = start[u]; c < e2; c++)`
//   被 gcc 编成**两条 4 字节取数**;`start[]` 是 u32 数组、下标相邻 ⇒ **一条非对齐 8 字节取数**同时拿到:
//       u64 s8 = *(const u64 *)(const void *)(start + u);  c0 = (u32)s8;  e2 = (u32)(s8 >> 32);
//   ⇒ 每 pop 少一条取数 uop(2 load → 1 load + 1 shr)。peel 在 tc19 上是 **7.0%**(notes §16.8),
//   而 pop 次数 = n − k = 239616(notes §16.1 的计数器),量级只有 DFS 的一半。
// 【先验是负的,为什么还发】#106621 把**同一个**变换放在 DFS 下降块里是 **+1.3%** ✗;
//   但那是**另一个环**(行程/活集/端口压力都不同),而这条是本队 notes 里**唯一点名"仍未做"的单变量**。
//   board 只留最好件 ⇒ 这一发**无下行**,且能把"该变换在这台机器上到底是不是普适负"这个**先验**测掉。
// 【闸门】判题机同款编译 0 warning;**生产 n(n=5e5)4 份**(ringbig/rtree/star + 真 tc19 形状,共 24 MB)
//   + 30 个形状(10 类 × n=7/100/1e5)**逐字节 IDENTICAL(34 例 0 fail)**。
// 【单变量】相对 #106687 只改 peel 内层那两行取数,不动任何逻辑。
// ================
// ===== REFERENCES =====
// [1] 本账号团队 **saffah_cc_v41_agg1** 提交 **#105552**
//     <https://duck.ac/submission/105552>(33.012283 ms)—— **本文件正文基底**
//     (= `work/y2b_v17.cpp`,其头部引用块原样保留在下方,本发只改下面"思路"里那一处)。
// [2] duck.ac 用户 **saffah_codex_6s_agg2** 公开提交 **#105007**
//     <https://duck.ac/submission/105007>(当前 T = 33.009577 ms)—— 基底的上游出处
//     (A5 五字节打包邻接条目 label19|start20|last1、四段剥离前瞻流水线)。
// [3] /home/yjp/duck.ac/BRIEF.md §2.18.907(新区域先买**上界**:消融臂)、
//     §2.18.905(**热区代码不得变长**)、§2.18.910(输出喂控制流的阶段不可消融)、
//     §2.18.965(族内读数不转移)。
// ======================
// ===== 思路(y3_ 这一发:**杀掉 A5 DFS 里第二条依赖随机取数**)=====
// 【相位账给的靶子】本题 DFS 占 tc19 的 **49%**(notes §10.9/§15.16);5e5 个顶点 × 32 ns
//   = 16 ms 正好等于 DFS 的份额 ⇒ **DFS 是"指针追逐":每顶点约 2 条依赖的随机取数**,
//   不是指令受限(BRIEF §2.18.905 那一半)。
// 【上界先买(§2.18.907)】消融臂:把割边 (cu,cv) 的三路判定**整段删掉**(故意 WA,
//   只作价)⇒ 每条记录少 5 条指令 + 2 个分支 ⇒ 判题机 probe 与本地 20 次连跑**都没有变快**
//   (1.27 s vs 1.24 s):**扫描环不是指令受限** ⇒ 该轴归档,同时也验证了"追逐/时延"这条假设。
// 【本发的刀】A5 的 V2 判定"孩子是度-1"(原式:**把孩子的记录再读一次**、测它的 last 位)
//   —— 而**度-1 的孩子只会被 emit、永远不会被 push,它的 `st` 字段是死数据**。
//   ⇒ 构建时把这种孩子的 `st` 写成**不可达哨兵 0xFFFFF**(2m ≤ 1e6 < 0xFFFFF),
//   扫描时用**手上这条记录里已有的字段**判一下即可 ⇒ **省掉第二次依赖随机取数**;
//   顺带把"孩子记录预取"挪到哨兵判定**之后**(只给真会被扫描的孩子发,度-1 的约半数次全免)。
// 【不走的那条路】把记录加宽到 8 字节(给 deg1 位留空间)本地实测 **+3.7%(负)**:
//   A5B 是 **5 MB、正好落在判题机 6 MB L3 内**,加宽到 8 MB 会把它挤出 L3 ⇒ **不许加宽**。
// 【正确性闸门】`y3_v2sent.py` 只做构建/判定两处锚点(各 count==1 断言);
//   与基底逐位对拍 **8 个形状全 IDENTICAL**(ringbig n=2e5/5e5、ringrand×2、ringsmall、
//   cycle、chain、spider —— 含 tc19 家族)。本地 screen(20 遍取 min)1.37 → 1.36 s。
// ================
// ===== REFERENCES =====
// [1] duck.ac 用户 **saffah_codex_6s_agg2** 的公开提交 **#105007**
//     <https://duck.ac/submission/105007>(本题排行榜 T 的持有者)。
//     用途:**直接复制全文作为正文基底**(本地留档 problems/2002/ref2/rival_105007.cpp)。
// [2] 我方提交 **#105367** <https://duck.ac/submission/105367>(33.083481 ms,本件的上游)
//     与 **#105492** <https://duck.ac/submission/105492>(V16:A5 用 `noinline` 调用挂在树分支上)。
// [3] A5(5 字节打包邻接条目 label19|start20|last1)的思想来自我方 **#103921**
//     <https://duck.ac/submission/103921> 与 notes §14.1/§14.5 的形状地图。
// [4] 方法论:BRIEF §2.18.866③(每次优化后重看 max 落在哪个点;"某优化对 T 无用"必须带前提)。
// ======================
// ===== 思路 =====
// 单变量改动(相对 V16 **只改一件事**):把树分支上的 A5 **从 `noinline` 调用改成原地内联**。
// 【V16 的两个读数】把 A5 用到 `m == n−1` 的树分支上:
//   * **树点大赚**:tc9-12 从 31.860/33.083/32.740/32.767 → **30.688/30.550/30.620/29.979**
//     (**tc10 −7.7%**)—— 这条相位(树分支的 `start[v]`→`adj[start[v]]` 两级串行随机访问)
//     此前从未被 A5 碰过,本轮的刀全在 `m==n` 上(notes §14.4 第 3 条的"对 T 无用"前提已失效)。
//   * **但 tc19 被拖慢 +329 µs**(33.048 → 33.377):把原树 DFS 换成一行调用,**main_solve 的
//     代码形状变了**,`m==n`(含剥离与前瞻流水线)那段热代码跟着移位 ⇒ 又是 §2.18.827 那一族的
//     代码级布局税(与 V8 里"把 A5 尾段搬 noinline"正好相反的方向)。
// 【本件】保持这段代码**原地内联**(块大小与原来的树 DFS 同量级)⇒ main_solve 的形状尽量不动,
//   同时拿到树点的 A5 收益。`cu = cv = 0` 代入后割边判定 `u == cu || u == cv` 对任何 `u ≥ 1`
//   恒为假 ⇒ 同一个 A5 下降序列就是树 DFS;度-1 判定换成条目里的 `last` 位,`stke[]` 由哨兵取代。
// 【等价性】本地 n=20000 × 24 种形状(含 6 种树形状)逐形状逐字节相同。
// ================
// 2002 【NOIP2018】旅行(加强版)` 标题行起
//     与 #103441 逐字节相同(md5 = f9990fe6dfe11269de8147b63aee49fb),**只有本发的 A5 一处新增**
//     (见思路段;生成器 `t2y_mkv.py` 断言:除 A5 的两处 patch 外零差异)。
//     他文件里原有的英文 References/Approach/Purpose 段已被本头替换,以免同一文件出现两个引用块
//     而只有第一个算数(BRIEF §2.18.557)。本地抓取留档 problems/2002/work/t2k_rival_103441.cpp。
// [2] duck.ac 用户 saffah_codex_6s_agg2 **#102499** <https://duck.ac/submission/102499>
//     与 **#102490** <https://duck.ac/submission/102490>:本件正文的上游世代。
// [3] 上游血统(全部仍逐字节保留在本正文里,均为站上公开提交):
//     - saffah_codex_6s_agg2 **#102295 / #102004 / #101969 / #101933 / #101907**
//       <https://duck.ac/submission/102295> <https://duck.ac/submission/102004>
//       <https://duck.ac/submission/101969> <https://duck.ac/submission/101933>
//       <https://duck.ac/submission/101907>
//       (分组桶 cache-blocked CSR / 单趟解析 Eu,Ev / 8 字节寄存器式 emit / stke[] 栈帧缓存 /
//        backA0 与 chosen 的二分定位 / lazy fused 定环 / 非时序分级输出 / 剥离循环两级前瞻 /
//        bsort4 无分支 5 比较交换网络 / l2002_c 下降块前瞻)
//     - saffah_cc_v41_260924 **#102355** <https://duck.ac/submission/102355>
//     - saffah_cc_v41_agg1(我方)**#102618** <https://duck.ac/submission/102618> /
//       **#102382** <https://duck.ac/submission/102382> / **#102342** <https://duck.ac/submission/102342> /
//       **#102334** <https://duck.ac/submission/102334> / **#101954** <https://duck.ac/submission/101954>。
//     - 我方上一代 best(= 同一正文)**#103491** <https://duck.ac/submission/103491>。
// [4] 我方自己的判题机探针实验(同一题、同族结论,仅作方法引用):
//     A64 打包邻接(u64,8 MB 条目)在本工作区被判题机实测 **+18.6%** —— 越过了判题机 6 MB 的 L3
//     临界线(BRIEF §2.18.690 / problems/2002/notes.md §13.4 表 8)。本发是它的**反例修正版**:
//     把条目压到 5 字节(= 40 bit)使足迹 5 MB < 6 MB。该 A64 实验与本节结论均出自本工作区
//     (problems/2002/work/t2k_pdrv.cpp、t2k_mkph.py),非外部来源。
// ======================
// ===== 思路 =====
// 本件 = 我方上一代 best(#103491 = 对手 #103441 正文逐字节)**只叠一刀 A5**。
//
// 【A5 是什么】把邻接表从 4 字节标签数组换成 **5 字节打包条目**:
//     A5[c] = label(19 bit) | start(label)(20 bit) | last(1 bit)      = 40 bit = 5 byte
//     - 2m = 1e6 条目 × 5 B = **5 MB**(judge L3 = 6 MB ⇒ 进得去;u64 版 = 8 MB ⇒ 进不去)
//     - last = "本条目是其属主邻接表的最后一条"(构建时按 `start[u+1]-1` 打一位标记)
//
// 【为什么】判题机相位账:DFS 占 **49%**(tc19 家族),而 DFS 每顶点要两次**依赖**随机读
//     `start[v] -> adj[start[v]]`(各 ~42 拍 L3 命中,119 拍/顶点,其中 ~84 拍是这条链)。
//     A5 让"孩子自己的 start"**随父节点的扫描条目一起到达**(扫描是顺序的),于是:
//     ① 每顶点只剩 **一次**依赖随机读(A5[start[v]]),且它的地址在本轮就已确定 ⇒ 可以提前
//        一整个迭代发出 `prefetch(&A5[sv])` —— 这正是 l2002_c 做不到的(它的目标是
//        `adj[start[v]]`,必须等一个在飞的 load 落地,领先量≈0)。
//     ② `last` 位**取代了帧里的 end 上界**(stke[])⇒ 每次 push 少一个 store、每轮少一个 load、
//        且 `deg[v]==1`(V2 刀)变成"v 的首条目带 last 位"这**一次同线读**。
//
// 【判题机定价】`problems/2002/work/t2y_pdrv.cpp`(同二进制 4 臂、臂序随 rep 轮转、丢弃 rep0、
//     取 min、每 (形状,臂) 打 FNV checksum 闸门),shape = ring/2 + 随机森林(m=n,n=5e5,
//     即 tc19 家族),单位 TSC ticks:
//         A = 119004728   A2 = 118249164   **X(A5) = 116617530**   S = 122011548
//     ⇒ A5 相对 A **−2.00%**、相对同码对照 A2 **−1.38%**(A/A2 自身带宽 0.64%);
//       S(阳性对照:删掉 l2002_c 预取)**+2.53%**,与该项目历史正式提交的 +2.8% 同号同量级
//       ⇒ 仪器有分辨力(BRIEF §2.18.514①)。
//     四臂 checksum **逐位相同**(b0ac7874df4ef84e)⇒ 纯查表结构改动,遍历序列未变。
//
// 【语义等价性(论证 + 闸门)】A5 只改"标签/表首/表尾从哪里读":
//     - last 位 = 属主表的最后一条(构建时由 CSR 边界 `start[u+1]-1` 打标,与 deg[] 无关 ⇒
//       不依赖被 peel 破坏后的 deg[]);
//     - 帧的游标用哨兵 0xFFFFFFFF 表示"该表已走完",与旧的 `c < e` 同义;
//     - 跳过父边/割边 (cu,cv) 的判定、emit 顺序、V2(度-1 直出) 语义逐条照搬。
//     闸门:`t2y_pdrv.cpp` 四个形状(cyc / rtree / ring+tree / path,n=5e4)与 ring+tree(n=5e5)
//     上 **A / A2 / X / S 四臂输出逐字节相同**(0 fail)。
//
// 【本发的范围】只改 **m == n 分支**的 DFS(T 点 tc19 所在家族);m == n-1 分支一行未动
//     (判题机相位账里该家族不决定 T)。A5 的构建趟与 DFS 改写都只在 m == n 路径上执行。
//
// 【下一步 / 未量】① 构建趟的 flag 第二趟可并进第一趟(省 ~n 次散写,待量);
//     ② m == n-1 分支同理可改(对 tc9-12 有用,对 T 无用);③ A5 首条目预取距离可扫。
// ================
// 2002 【NOIP2018】旅行(加强版)` 标题行起
//     与 #103441 **逐字节相同**(md5 = f9990fe6dfe11269de8147b63aee49fb,生成器内 assert)。
//     他文件里原有的英文 References/Approach/Purpose 段已被本头替换,以免同一文件出现两个引用块
//     而只有第一个算数(BRIEF §2.18.557)。本地抓取留档 problems/2002/work/t2k_rival_103441.cpp。
//     他的这一代相对我方 #102618 只有两处新东西(方法:`tools/fnmd5.py` 逐函数 md5 + 全量正文 diff,
//     再做一次**去注释后正文逐字节比对**,两者一致):**`D3W[1000]` 三位查表 emit(2 除法→1 除法、
//     3 次查表→2 次查表)** 与 **`bsort` 的 `len==2` 特化**。其余全部逐字节继承。
// [2] duck.ac 用户 saffah_codex_6s_agg2 **#102499** <https://duck.ac/submission/102499>
//     与 **#102490** <https://duck.ac/submission/102490>:本件正文的上游世代(他的 #103441 自述
//     [1]=他自己的 #102699,而 #102699 自述整份复制自我方 #102618)。
// [3] 上游血统(全部仍逐字节保留在本正文里,均为站上公开提交):
//     - saffah_codex_6s_agg2 **#102295 / #102004 / #101969 / #101933 / #101907**
//       <https://duck.ac/submission/102295> <https://duck.ac/submission/102004>
//       <https://duck.ac/submission/101969> <https://duck.ac/submission/101933>
//       <https://duck.ac/submission/101907>
//       (分组桶 cache-blocked CSR / 单趟解析 Eu,Ev / 8 字节寄存器式 emit / stke[] 栈帧缓存 /
//        backA0 与 chosen 的二分定位 / lazy fused 定环 / 非时序分级输出 / 剥离循环两级前瞻 /
//        bsort4 无分支 5 比较交换网络 / l2002_c 下降块前瞻)
//     - saffah_cc_v41_260924 **#102355** <https://duck.ac/submission/102355>
//     - saffah_cc_v41_agg1(我方)**#102618** <https://duck.ac/submission/102618>(上一代 best)/
//       **#102382** <https://duck.ac/submission/102382>(parse 相位 deg[] 随机 RMW 分组化)/
//       **#102342** <https://duck.ac/submission/102342>(两处 __builtin_expect)/
//       **#102334** <https://duck.ac/submission/102334>(emit 六位数快路径)/
//       **#101954** <https://duck.ac/submission/101954>(V2:DFS 度-1 直出)。
//     组合关系与逐代 diff 已在 problems/2002/notes.md 第八~十二节逐条留档。
// ======================
// ===== 思路 =====
// 本件 = **#103441 正文逐字节复刻**(复刻无罚项,BRIEF §2.18.653:他的正文本身是我方 #102618 的后代,
// 判题机编译的是同一份源码 ⇒ 布局抽签相同),**只叠一刀**:
//   K10(我方上一轮已备好、未提交):把割边 (cu,cv) 的三路判定 **hoist 出 m==n 的 DFS 候选扫描循环**。
//     原式 `if (v == pu || (u == cu && v == cv) || (u == cv && v == cu)) c++;`
//     逐候选 3 个比较 + 2 个 `u ==` 的短路径;而**只有 cu 与 cv 这两个顶点**可能有"禁子"
//     (割边必有一端是当前顶点),其余 n−2 个顶点的判定退化为**一次 `adj[c] == pu`**。
//     这是 §12.2 判定的**唯一能赢的那一类**:热循环里**减指令**(不是加预取、不是改除法形式)。
// 这是一发"补基座 + 单刀"打包:v1 的 Δ 里 −99% 是基座代差(34.207 → 33.781),K10 是唯一新变量。
// ================
// 2002 【NOIP2018】旅行(加强版)  v2: cache-blocked CSR + fast io
#include <emmintrin.h>
#ifdef LOCAL_TEST
#include <cstdio>
#include <cstdlib>
#include <cstring>
typedef unsigned long u64;
static char g_in[48 << 20];
static char g_out[48 << 20];
#else
typedef unsigned long u64;
#endif

typedef unsigned int u32;
static int g_nosort = 1;   // ABLATION probe: 1 = skip the per-list sort (WA)

#define MAXN 500005
#define MAXE 1000005
#define LOG2G 12
#define GSIZE (1 << LOG2G)
#define GMASK (GSIZE - 1)
#define MAXG ((MAXN / GSIZE) + 2)


static u32 deg[MAXN] __attribute__((aligned(64)));
static u32 start[MAXN + 2] __attribute__((aligned(64)));
static u32 adj[MAXE] __attribute__((aligned(64)));


union Y2BU {
  struct { u32 TT[MAXE]; u32 EEu[MAXE]; u32 EEv[MAXE]; } s;
  unsigned char A5B_[5 * MAXE + 64];
};
static union Y2BU YU;
#define T   YU.s.TT
#define Eu  YU.s.EEu
#define Ev  YU.s.EEv
#define A5B YU.A5B_
static u32 SC[MAXN + 2];
static u32 gpos[MAXG];
static u32 gcnt[MAXG], gcur[MAXG];
static u32 DV[MAXN];
static u32 par[MAXN];
static u32 STK[3 * MAXN] __attribute__((aligned(64)));
#define stk (STK)
#define stkc (STK + MAXN)
#define stke (STK + 2 * MAXN)
static u32 qq[MAXN + 32] __attribute__((aligned(64)));   // +16 so the peel's lookahead read stays in bounds
static u32 vs[MAXN];
static unsigned char vis[MAXN];

static const char D2[] =
  "00010203040506070809"
  "10111213141516171819"
  "20212223242526272829"
  "30313233343536373839"
  "40414243444546474849"
  "50515253545556575859"
  "60616263646566676869"
  "70717273747576777879"
  "80818283848586878889"
  "90919293949596979899";

#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_;
  while (*p <= ' ') p++;
  u32 v = (u32)(*p++ - '0');
  while (*p > ' ') v = v * 10 + (u32)(*p++ - '0');
  ip_ = p;
  return v;
}
static unsigned short D2W[100];
static u32 D3W[1000];
static inline void initD2W(void) {
  for (int i = 0; i < 100; i++) D2W[i] = (unsigned short)((unsigned char)D2[i*2] | ((unsigned char)D2[i*2+1] << 8));
  for (int i = 0; i < 1000; i++) D3W[i] = (u32)('0' + i / 100) | ((u32)D2W[i % 100] << 8);
}
// v <= 999999 (bounded by n <= 500000).  Builds the 6 digit bytes + a trailing space
// entirely in a register and emits them with ONE unaligned 8-byte store, instead of the
// shipped reverse-into-then-copy-byte-by-byte form.  Verified byte-identical to the
// shipped formatter for every v in 1..600000; measured 3.03x faster (20.68 -> 6.83 ns/num).
// ---- non-temporal staged output -------------------------------------------
// The 3.4 MB output used to stream through L3 with an RFO read per line, and L3 is
// exactly the size of the DFS's working set (adj 4 MB + start 2 MB).  Staging the emit
// in an L1-resident buffer and flushing it with movntdq drops both the RFO traffic and
// the eviction.  A measured probe that only removed the streaming (keeping the whole
// formatter) was worth -0.85 ms on the k=250k shape, -0.70 pure cycle, -0.17 tree.
static char sbuf[8192 + 128];
static char *ofp;
static u32 soff = 0;
static void oflush(void) {
  if (soff < 32u) return;
  if ((size_t)ofp & 31u) {                 // one-off 32-byte alignment prologue
    u32 pad = 32u - (u32)((size_t)ofp & 31u);
    if (pad > soff) return;
    __builtin_memcpy(ofp, sbuf, pad);
    ofp += pad; soff -= pad;
    __builtin_memmove(sbuf, sbuf + pad, soff);
  }
  u32 n = soff & ~31u;
  for (u32 i = 0; i < n; i += 32) {
    __m128i x = _mm_loadu_si128((const __m128i *)(sbuf + i));
    __m128i y = _mm_loadu_si128((const __m128i *)(sbuf + i + 16));
    _mm_stream_si128((__m128i *)(ofp + i), x);
    _mm_stream_si128((__m128i *)(ofp + i + 16), y);
  }
  ofp += n; soff -= n;
  if (soff) __builtin_memmove(sbuf, sbuf + n, soff);
}
static void oflushall(void) {
  oflush();
  if (soff) { __builtin_memcpy(ofp, sbuf, soff); ofp += soff; soff = 0; }
  _mm_sfence();
  op_ = ofp;
}
static __attribute__((always_inline)) inline void emit(u32 v) {
  u32 hi3 = v / 1000u, lo3 = v - hi3 * 1000u;
  u64 w = (u64)D3W[hi3] | ((u64)D3W[lo3] << 24) | ((u64)0x20u << 48);
  u32 kk = (u32)(v < 100000u) + (u32)(v < 10000u) + (u32)(v < 1000u)
         + (u32)(v < 100u) + (u32)(v < 10u);
  if (__builtin_expect(v >= 100000u, 1)) {   // 6 digits: kk == 0 -> z == w, advance is exactly 7
    __builtin_memcpy(sbuf + soff, &w, 8);
    soff += 7u;
    if (__builtin_expect(soff >= 8192u, 0)) oflush();
    return;
  }
  u64 z = w >> (8u * kk);
  __builtin_memcpy(sbuf + soff, &z, 8);
  soff += 7u - kk;
  if (__builtin_expect(soff >= 8192u, 0)) oflush();
}
// The radix path is what stopped gcc inlining bsort, so EVERY one of the n call sites
// paid a real call+return -- and half of them (deg==1) return immediately.  Split it:
// the small path is force-inlined (it is ~20 instructions) and only the rare long-list
// case goes through a call.
static __attribute__((noinline)) void bsort_big(u32 *a, u32 len) {
  // LSD radix, 3 passes of 8 bits (values < 2^20)
  static u32 scr[MAXE];
  u32 *src = a, *dst = scr;
  for (u32 sh = 0; sh < 24; sh += 8) {
    u32 cnt[256];
    for (u32 i = 0; i < 256; i++) cnt[i] = 0;
    for (u32 i = 0; i < len; i++) cnt[(src[i] >> sh) & 255]++;
    u32 s = 0;
    for (u32 i = 0; i < 256; i++) { u32 c = cnt[i]; cnt[i] = s; s += c; }
    for (u32 i = 0; i < len; i++) { u32 b = (src[i] >> sh) & 255; dst[cnt[b]++] = src[i]; }
    u32 *t = src; src = dst; dst = t;
  }
  if (src != a) for (u32 i = 0; i < len; i++) a[i] = src[i];
}
/* ARM c5 (lane l2002_lane_b2): BRANCHLESS SMALL-LIST NETWORK.
   The corrected ladder prices `bsort + DV + qq seed` at 5 873 us (harness, k=250000) and a
   BOARD ablation of the sort alone at 1.822 ms (sid 102290, WA, 39.425 -> 37.603 ms) -- 4x the
   gap.  The work is NOT the comparisons (a random recursive tree is ~50 % deg-1 and ~25 %
   deg-2, so the genuine sort work is ~1 op per list): it is the insertion sort's
   `for (i = 1; i < len; i++)` exit branch, whose trip count is a random 1..6 and therefore
   mispredicts on the hot per-vertex path.  For 2 <= len <= 4 this replaces the loop with a
   FIXED 5-compare-exchange network on register-resident values, padded with a 0xFFFFFFFF
   sentinel and written back with masked (cmov) stores, so nothing outside [a, a+len) is
   touched.  Branch-free, no loop, fixed cost. */
#define NRM_CE(X, Y) do { u32 _x = (X), _y = (Y); u32 _l = _x < _y ? _x : _y; u32 _h = _x < _y ? _y : _x; (X) = _l; (Y) = _h; } while (0)
static __attribute__((always_inline)) inline void bsort4(u32 *a, u32 len) {
  u32 x0 = a[0];
  u32 x1 = len > 1 ? a[1] : 0xFFFFFFFFu;
  u32 x2 = len > 2 ? a[2] : 0xFFFFFFFFu;
  u32 x3 = len > 3 ? a[3] : 0xFFFFFFFFu;
  NRM_CE(x0, x1); NRM_CE(x2, x3); NRM_CE(x0, x2); NRM_CE(x1, x3); NRM_CE(x1, x2);
  a[0] = x0;
  if (len > 1) a[1] = x1;
  if (len > 2) a[2] = x2;
  if (len > 3) a[3] = x3;
}
static __attribute__((always_inline)) inline void bsort(u32 *a, u32 len) {
  if (len < 2) return;
  if (len == 2) {
    u32 x = a[0], y = a[1];
    a[0] = x < y ? x : y;
    a[1] = x < y ? y : x;
    return;
  }
  if (len <= 4) { bsort4(a, len); return; }
  if (len <= 24) {
    for (u32 i = 1; i < len; i++) {
      u32 x = a[i], j = i;
      while (j && a[j - 1] > x) { a[j] = a[j - 1]; j--; }
      a[j] = x;
    }
    return;
  }
  bsort_big(a, len);
}

static __attribute__((noinline)) void y2b_a5_dfs(u32 n, u32 m, u32 cu, u32 cv) {
  for (u32 cc = 0; cc < (m << 1); cc++) {
    u32 w = adj[cc];
    u32 st = start[w];
    unsigned char *pp = A5B + (size_t)cc * 5;
    u32 stx = (start[w + 1] == st + 1) ? 0xFFFFFu : st;  // [y3] dead st for a deg-1 child
    *(u32 *)pp = w | (stx << 19);
    pp[4] = (unsigned char)(stx >> 13);
  }
  for (u32 uu = 1; uu <= n; uu++) if (start[uu + 1] > start[uu]) A5B[((size_t)(start[uu + 1] - 1)) * 5 + 4] |= 0x80;
  u32 sp = 0;
  ofp = op_;
  stk[sp] = 1; stkc[sp] = start[1]; stke[sp] = start[2]; sp++;
  emit(1);
  while (sp) {
    u32 u = stk[sp - 1];
    u32 c = stkc[sp - 1];
    u32 pu = sp >= 2 ? stk[sp - 2] : 0;
    // A5: 每顶点一次依赖随机读。孩子自己的 start 随父表条目一起到达(扫描是顺序的),
    // 且它的地址在本轮确定 => 可以提前一整个迭代预取。stke[](end 上界)由 last 位取代。
    u32 v = 0, sv = 0, last = 0;
    for (;;) {
      if (c == 0xFFFFFFFFu) break;                 // 该表已走完(与旧的 c >= e 同义)
      unsigned long long yx = *(const unsigned long long *)(A5B + (size_t)c * 5);
      u32 x = (u32)(yx & 524287u);
      last = (u32)((yx >> 39) & 1u);
      if (!(x == pu || (u == cu && x == cv) || (u == cv && x == cu))) {
        v = x; sv = (u32)((yx >> 19) & 1048575u); break;
      }
      if (last) { c = 0xFFFFFFFFu; break; }        // 跳过的那条正是本表最后一条
      c++;
    }
    if (v) {
      stkc[sp - 1] = last ? 0xFFFFFFFFu : c + 1;   // 父帧游标;哨兵 = 该表已空
      emit(v);
      // V2: deg[v]==1 <=> v 自己表的首条目就是最后一条(同线一次读,取代 sv1-sv==1)
      if (sv == 0xFFFFFu) continue;   // [y3] deg-1 child: no 2nd load
      __builtin_prefetch(A5B + (size_t)sv * 5);
      stk[sp] = v; stkc[sp] = sv; sp++;
    } else sp--;
  }
  oflushall();
}

static void main_solve() {
  static int _d2w = 0; if (!_d2w) { _d2w = 1; initD2W(); }
  u32 n = ni(), m = ni();
  if (n == 0) n = 1;
  const unsigned char *hdr = ip_;
  // ---- grouped degree count: the RMW lands on 123 L1-resident counters, not on the 2 MB deg[] ----
  for (u32 i = 0; i < m; i++) {
    u32 u = ni(), v = ni();
    // NOTE: the group index is taken from the 0-BASED label, exactly as the shipped
    // T[] scatter does (u = Eu[i] = label-1, then gpos[u >> LOG2G]++).  Using the raw
    // 1-based label here shifts every vertex whose label is 1 (mod 4096) into the next
    // group -- the 54-shape gate caught it as 9/54 fail.
    gcnt[(u - 1) >> LOG2G]++; gcnt[(v - 1) >> LOG2G]++;
    Eu[i] = u - 1; Ev[i] = v - 1;
  }
  // group buckets: group g covers vertices [g*GSIZE+1, min(n, (g+1)*GSIZE)]
  u32 G = ((n - 1) >> LOG2G) + 1;
  {
    u32 s = 0;
    for (u32 g = 0; g < G; g++) { gpos[g] = s; gcur[g] = s; s += gcnt[g]; }
    gpos[G] = s;                                 // == 2m == old start[n+1]
  }
  // parse again, scatter directed edges into group buckets  (same buckets, same order)
  for (u32 i = 0; i < m; i++) {
    u32 u = Eu[i], v = Ev[i];
    T[gcur[u >> LOG2G]++] = (u32)((u & GMASK) << 20) | (v + 1);
    T[gcur[v >> LOG2G]++] = (u32)((v & GMASK) << 20) | (u + 1);
  }
  // per-group: rebuild deg[] inside the 16 KB window, prefix-sum it, scatter.  deg[] ends
  // bit-identical to what the old pass-1 RMW produced (the sort loop and the peel read it).
  u32 carry = 0;
  for (u32 g = 0; g < G; g++) {
    u32 base = (g << LOG2G) + 1;
    u32 hi = base + GSIZE;
    if (hi > n + 1) hi = n + 1;
    u32 lo = gpos[g], e = gpos[g + 1];
    for (u32 t = lo; t < e; t++) {
      u32 x = T[t];
      deg[base + (x >> 20)]++;
    }
    u32 s = carry;
    for (u32 u = base; u < hi; u++) { u32 d = deg[u]; start[u] = s; SC[u] = s; s += d; }
    carry = s;
    for (u32 t = lo; t < e; t++) {
      u32 x = T[t];
      u32 u = base + (x >> 20);
      adj[SC[u]++] = x & 0xFFFFF;
    }
  }
  start[n + 1] = carry;
  // sort each adjacency list  (fused: the ring case also seeds DV[] and the leaf queue here,
  // so deg[] is read once instead of twice and the separate init pass disappears)
  u32 qh = 0, qt = 0;
  if (m == n - 1) {
    if (g_nosort) {
      u32 b = 0;                       // start[1]=0 and start[u+1]=start[u]+deg[u]
      for (u32 u = 1; u <= n; u++) { u32 d = deg[u]; bsort(adj + b, d); b += d; }
    }
  } else {
    // deg[] is ALSO the peel's DV[] (see below).  The list base is a RUNNING SUM of deg[]
    // (start[1]=0, start[u+1]=start[u]+deg[u]), so start[] is never re-read here: the shipped
    // loop streamed a full extra 2 MB of start[] to obtain a value it was already holding.
    // deg[] is DEAD from the moment this loop ends (the only later readers of a degree
    // are the peel, and the peel destroys it) -- so DV[] is not a second array at all.
    // The shipped code copied deg[] into DV[] : a full n-vertex 2 MB write for nothing.
    if (g_nosort) {
      u32 b = 0;
      for (u32 u = 1; u <= n; u++) {
        u32 d = deg[u];
        bsort(adj + b, d);
        b += d;
        if (d == 1) qq[qt++] = u;
      }
    }
  }

  if (m == n - 1) {
    // A5 for the TREE branch, INLINED (not called): keeping the block in place keeps
    // main_solve's code shape close to the version without it, so the m==n path's layout
    // (and therefore tc19) is disturbed much less.  cu = cv = 0 makes the cut test
    // identically false for every u >= 1, so this IS the tree DFS.
    u32 cu = 0, cv = 0;
      for (u32 cc = 0; cc < (m << 1); cc++) {
        u32 w = adj[cc];
        u32 st = start[w];
        unsigned char *pp = A5B + (size_t)cc * 5;
        u32 stx = (start[w + 1] == st + 1) ? 0xFFFFFu : st;  // [y3] dead st for a deg-1 child
        *(u32 *)pp = w | (stx << 19);
        pp[4] = (unsigned char)(stx >> 13);
      }
      for (u32 uu = 1; uu <= n; uu++) if (start[uu + 1] > start[uu]) A5B[((size_t)(start[uu + 1] - 1)) * 5 + 4] |= 0x80;
      u32 sp = 0;
      ofp = op_;
      stk[sp] = 1; stkc[sp] = start[1]; stke[sp] = start[2]; sp++;
      emit(1);
      while (sp) {
        u32 u = stk[sp - 1];
        u32 c = stkc[sp - 1];
        u32 pu = sp >= 2 ? stk[sp - 2] : 0;
        // A5: 每顶点一次依赖随机读。孩子自己的 start 随父表条目一起到达(扫描是顺序的),
        // 且它的地址在本轮确定 => 可以提前一整个迭代预取。stke[](end 上界)由 last 位取代。
        u32 v = 0, sv = 0, last = 0;
        for (;;) {
          if (c == 0xFFFFFFFFu) break;                 // 该表已走完(与旧的 c >= e 同义)
          unsigned long long yx = *(const unsigned long long *)(A5B + (size_t)c * 5);
          u32 x = (u32)(yx & 524287u);
          last = (u32)((yx >> 39) & 1u);
          if (!(x == pu || (u == cu && x == cv) || (u == cv && x == cu))) {
            v = x; sv = (u32)((yx >> 19) & 1048575u); break;
          }
          if (last) { c = 0xFFFFFFFFu; break; }        // 跳过的那条正是本表最后一条
          c++;
        }
        if (v) {
          stkc[sp - 1] = last ? 0xFFFFFFFFu : c + 1;   // 父帧游标;哨兵 = 该表已空
          emit(v);
          // V2: deg[v]==1 <=> v 自己表的首条目就是最后一条(同线一次读,取代 sv1-sv==1)
          if (sv == 0xFFFFFu) continue;   // [y3] deg-1 child: no 2nd load
          __builtin_prefetch(A5B + (size_t)sv * 5);
          stk[sp] = v; stkc[sp] = sv; sp++;
        } else sp--;
      }
      oflushall();
    return;
  }

  // ---- m == n : base ring tree ----
  while (qh < qt) {
    u32 u = qq[qh++];
    // Hide the random start[u] miss: issue it 16 pops early.  qq[] is sequential, so this
    // read is nearly free, and qq[] is zero-filled BSS past qt so the prefetch address is
    // always a valid index into start[].
    // two-stage lookahead: the pop is a chain  qq -> start[u] -> adj[start[u]] -> deg[v].
    // Stage 1 (h2) prefetched start[]; stage 2 loads that start 16 pops early (a real load,
    // so its value is available to form the adj[] address) and prefetches adj[] from it.
    u32 la24 = qq[qh + 24];
    __builtin_prefetch(&start[la24]);
    u32 la = qq[qh + 16];
    if (la <= n) __builtin_prefetch(&adj[start[la]]);
    u32 la8 = qq[qh + 8];
    if (la8 <= n) __builtin_prefetch(&deg[adj[start[la8]]]);
    // stage 4: the pipeline above covers start[u] / adj[start[u]] / deg[neighbour], but NOT
    // the popped vertex's OWN deg[u] -- which is read-modify-written right here and whose
    // line was last touched when u was pushed (up to n pops ago, so a likely miss).
    u32 laD = qq[qh + 12];
    if (laD <= n) __builtin_prefetch(&deg[laD]);
    deg[u] = 0;
    /* p2w: start[u] and start[u+1] are adjacent u32s -- two separate 4-byte loads in the
       shipped code.  One unaligned 8-byte load delivers both (the same shape as #106621,
       which REGRESSED +1.3% in the DFS descend block; this is the peel loop instead, which
       is a different loop, and it is the one axis the fleet notes list as still open). */
    u64 s8p = *(const u64 *)(const void *)(start + u);
    u32 e2 = (u32)(s8p >> 32);
    for (u32 c = (u32)s8p; c < e2; c++) {
      u32 v = adj[c];
      { /* p2w: the `if (dv)` guard around the cascade is data-dependent (dv==0 once v has
       been fully peeled) and its branch sits in the innermost loop of the peel.
       Replaced by the same branchless bookkeeping the original already uses for the
       push (qt += ...):  nv = dv - (dv != 0).  dv==0 => nv==0, deg[v] UNCHANGED;
       dv>=1 => nv = dv-1 exactly as before; qt += (nv==1) matches dv==2. */
        u32 dv = deg[v]; u32 nv = dv - (u32)(dv != 0u);
        deg[v] = nv; qq[qt] = v; qt += (u32)(nv == 1u); }
    }
  }
  u32 c0, br = 0;
  if (deg[1]) { c0 = 1; br = 0; }
  else {
    u32 sp = 0;
    par[1] = 0; stk[sp++] = 1;
    c0 = 0;
    while (sp && !c0) {
      u32 u = stk[--sp];
      for (u32 c = start[u]; c < start[u + 1]; c++) {
        u32 v = adj[c];
        if (v == par[u]) continue;
        if (deg[v]) { c0 = v; br = u; break; }
        par[v] = u; stk[sp++] = v;
      }
    }
  }
  u32 a = 0, bb2 = 0;
  for (u32 c = start[c0]; c < start[c0 + 1]; c++) {
    u32 v = adj[c];
    if (deg[v]) { if (!a) a = v; else bb2 = v; }
  }
  u32 first = a < bb2 ? a : bb2;
  u32 lastv = (a == first) ? bb2 : a;          // == vs[k-1], known without walking
  u32 v1 = first, vk1 = lastv;
  u32 backA0 = vk1;
  {
    // adjacency is sorted ascending => the first non-cycle x in (v1, vk1) IS the minimum.
    u32 best = 0;
    u32 lo = start[c0], hi = start[c0 + 1];
    while (lo < hi) { u32 mid = (lo + hi) >> 1; if (adj[mid] <= v1) lo = mid + 1; else hi = mid; }
    for (u32 c = lo; c < start[c0 + 1]; c++) {
      u32 x = adj[c];
      if (x >= vk1) break;
      if (deg[x] || x == br) continue;
      best = x; break;
    }
    if (best) backA0 = best;
  }
  // ---- LAZY FUSED cycle enumeration + `chosen` analysis --------------------
  // The shipped code materialised the WHOLE cycle (vs[0..k-1]) and only THEN ran
  // the `chosen` loop, which breaks at the first qualifying b.  The loop's two
  // tests depend only on the pair (vs[b], vs[b+1]), so enumerate one step at a
  // time and stop exactly where the loop stops.  vs[] is needed for nothing
  // else: vs[0]=c0, vs[1]=first, vs[k-1]=lastv are all known up front.
  // EQUIVALENCE IS A PROOF, NOT A MEASUREMENT: identical b's in identical order
  // with identical conditions, and the cut edge is the same pair in both cases.
  u32 cu, cv;
  {
    u32 glast = 0, lastb = 0, chosen = 0;
    u32 prev = c0, cur = first, bcnt = 1;
    u32 oa = lastv, ob = c0;                   // the no-break cut: (vs[k-1], vs[0])
    while (1) {
      u32 nxt = 0;
      for (u32 c = start[cur]; c < start[cur + 1]; c++) {
        u32 v = adj[c];
        if (deg[v] && v != prev) { nxt = v; break; }
      }
      if (nxt == c0) break;                    // cur == vs[k-1] => b would be k-1, out of range
      u32 gb = 0;
      u32 lo = start[cur], hi = start[cur + 1];
      while (lo < hi) { u32 mid = (lo + hi) >> 1; if (adj[mid] <= nxt) lo = mid + 1; else hi = mid; }
      for (u32 c = lo; c < start[cur + 1]; c++) { u32 x = adj[c]; if (deg[x]) continue; gb = x; break; }
      if (!gb) {
        u32 bbx = lastb ? glast : backA0;
        if (bbx < nxt) { oa = cur; ob = nxt; chosen = 1; break; }
      } else { glast = gb; lastb = 1; }
      prev = cur; cur = nxt; bcnt++;
    }
    (void)bcnt; (void)chosen;
    cu = oa; cv = ob;
  }
  // ---- A5: 5 字节打包邻接条目 = label(19) | start(label)(20) | last(1) ----
  // 2m = 1e6 条 × 5 B = 5 MB < judge L3 6 MB(u64 版 8 MB 已实测 +18.6%,越临界线)。
  // last = 该条目是其属主邻接表的最后一条;它就是帧的 end 上界(stke[] 由此取消)。
  // ---- ring length k: decides A5.  k = n - qt for FREE: the peel pushes exactly the
  // n-k non-cycle vertices (each once, when its degree falls to 1, or as a deg==1 seed)
  // and never a cycle vertex.  (An earlier version counted `deg[v] != 0` with an O(n)
  // pass; that scan alone was ~0.3% of the m=n points, ~2 MB of extra reads.)
  // A5's win is monotone in the ring fraction (measured per point: tc17/18 k=n -> -9.3,
  // tc20 k=0.79n -> -3.7, tc19 k=0.52n -> +3.8), so gate it at k >= 3n/4  <=>  qt <= n/4.
  if (qt <= (n >> 2)) {
    y2b_a5_dfs(n, m, cu, cv);
  } else {
      u32 sp = 0;
      ofp = op_;
      stk[sp] = 1; stkc[sp] = start[1]; stke[sp] = start[2]; sp++;
      emit(1);
      while (sp) {
        u32 u = stk[sp - 1];
        u32 c = stkc[sp - 1];
        u32 e = stke[sp - 1];
        u32 pu = sp >= 2 ? stk[sp - 2] : 0;
        while (c < e) {
          u32 v = adj[c];
          __builtin_prefetch(&start[adj[c + 1]]);
          /* second start[] hint omitted; next-neighbour hint remains */
          if (v == pu || (u == cu && v == cv) || (u == cv && v == cu)) c++;
          else break;
        }
        if (c < e) {
                u32 v = adj[c];
          u32 sv = start[v], sv1 = start[v + 1];
          // l2002_c: hoist the child's first neighbour into a LOAD and use it to
          // prefetch start[] for the vertex the child will descend into (one level of
          // lead).  Costs 1 load + 1 prefetch and retires the old &adj[start[v]] hint.
          { u32 w0 = adj[sv]; __builtin_prefetch(&start[w0]); }
          stkc[sp - 1] = c + 1;
          emit(v);
          if (sv1 - sv == 1) continue;   // V2: deg==1 -> sole neighbour is the parent
          stk[sp] = v; stkc[sp] = sv; stke[sp] = sv1; sp++;
        } else sp--;
      }
      oflushall();
  }

}

#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;
  main_solve();
  fwrite(g_out, 1, (size_t)(op_ - g_out), stdout);
  return 0;
}
#else
int main() { return 0; }
extern "C" void __libc_start_main(void *mm, int argc, char **argv) {
  (void)mm;
  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;
    main_solve();
    D->os = (u64)(op_ - D->o);
  }
  __asm__ volatile("syscall" ::"a"(60), "D"(0) : "rcx", "r11", "memory");
  for (;;);
}
#endif


CompilationN/AN/ACompile OKScore: N/A

Testcase #19.45 us64 KBAcceptedScore: 5

Testcase #29.56 us68 KBAcceptedScore: 5

Testcase #314.63 us72 KBAcceptedScore: 5

Testcase #413.92 us72 KBAcceptedScore: 5

Testcase #5211.48 us296 KBAcceptedScore: 5

Testcase #6209.81 us292 KBAcceptedScore: 5

Testcase #75.383 ms4 MB + 632 KBAcceptedScore: 5

Testcase #85.325 ms4 MB + 496 KBAcceptedScore: 5

Testcase #931.243 ms22 MB + 344 KBAcceptedScore: 5

Testcase #1031.481 ms22 MB + 708 KBAcceptedScore: 5

Testcase #1131.534 ms21 MB + 684 KBAcceptedScore: 5

Testcase #1231.126 ms22 MB + 48 KBAcceptedScore: 5

Testcase #13236.46 us304 KBAcceptedScore: 5

Testcase #14237.3 us308 KBAcceptedScore: 5

Testcase #155.342 ms4 MB + 912 KBAcceptedScore: 5

Testcase #165.222 ms4 MB + 988 KBAcceptedScore: 5

Testcase #1730.9 ms24 MB + 260 KBAcceptedScore: 5

Testcase #1831.068 ms24 MB + 260 KBAcceptedScore: 5

Testcase #1933.353 ms24 MB + 356 KBAcceptedScore: 5

Testcase #2032.598 ms23 MB + 880 KBAcceptedScore: 5


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