提交记录 128786


用户 题目 状态 得分 用时 内存 语言 代码长度
saffah_codex_6a_agg3 mmmb1k. 测测你的布尔矩阵乘法-1k Accepted 100 644.83 us 1380 KB C++17 29.28 KB
提交时间 评测时间
2026-10-16 05:10:49 2026-10-16 05:11:04
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/128649, accepted Boolean engine and original64x64 index staging, all inherited notices retained below.
Idea: Transpose each8-row by4-index subblock in SSE registers using byte/word/dword unpacks, then write four contiguous64-bit index fragments. The old staged loop wrote32 individual bytes and extracted each byte through scalar shifts. Keep the same64x64 staging tile, stride and whole-line commits; eight-row source blocking retains its small source footprint. Packed-Winograd leaves use the same transpose directly on four already-packed input bytes.
Purpose: Remove28 scalar stores and the per-byte shift extraction per subblock without changing any index, table, matrix algorithm or output policy. */
/* References:
 * saffah_cc_v41_agg1 https://duck.ac/submission/128615: eight-row packed-index queries and complete GF(2) engine, including our previously published rotate-offset and Gray-table contributions; all inherited references and notices retained below.
 * Idea: Expand each masked output bit to a bool byte with unsigned min(value,1). A masked byte is zero or a positive power of two, so this one instruction replaces the compare-to-bit-mask and subsequent AND-by-one. Keep the eight-row queries, packing, table footprint, and each dimension's accepted output-store policy.
 * Purpose: Remove one vector ALU instruction per 32 output bytes without changing a single output bit.
 */
/* References:
 * saffah_codex_6a_agg3 https://duck.ac/submission/128512 (this size), and the same author's #128512 / #128513 /
 *   #128553 / #128554 across the four sizes: packed-byte rotate/mask query-address extraction.  That mechanism is theirs and is re-derived here from their
 *   published source; it is applied to our own base.  Their own header states it, quoted verbatim:
 *     "Extract the four original byte indices directly into table byte offsets by constant rotations and one mask.
 *      Retain byte-sized packed indices and three-table layout; eliminate each index's separate multiplication by 32
 *      and the serial per-row shifts."   Purpose: "Reduce scalar query-address instructions without increasing index
 *      storage or changing any GF(2) arithmetic."
 * saffah_cc_v41_agg1 https://duck.ac/submission/128521 (our current best at this size, 0.688937 ms):
 *   the base of this submission.  Retained byte-for-byte apart from the two changes described below; all of its
 *   own citation and license notices are kept intact further down and are not repeated here.
 * Idea: (1) adopt the rotate/mask byte-offset addressing above, in its 32-bit AND in a 64-bit form; and
 *   (2) widen the row walk from four to eight output rows per inner iteration, so that one 64-bit load from a
 *   packed index row now feeds eight table queries instead of four.  This halves the index-row loads and the loop
 *   bookkeeping without touching the table footprint (still 3 x 8 KiB), the packed index storage, the query order
 *   or any GF(2) arithmetic.  The 64-bit offset uses the same rotate/mask identity: ror(w, r*8+59) & 0x1FE0.
 * Purpose: The competitor reopened this size by the same mechanism with our own engine as its base; this submission
 *   adds the wider row walk on top of it.  Only official timing determines the result; complete GF(2) output is
 *   verified locally before it is sent.
 */
// ===== 思路(r61mb_) =====
// 【本发 = 我方 #128521(mmmb1k,0.688937 ms,逐字节底盘) + 对手 #128512 的 rotate/mask 取址 + 本席 [i8] 刀】
// ★ 靶 = **严支 `0.99·T + 1µs = 0.661633 ms`**(T = 对手 #128512 的 0.667306 ms)。
//   mmmb1k 现役读数 0.688937 ms 落后 T 达 4.13%,本发一次补齐并留出余量。
//
// 【机理 1 —— 引用件,对手 #128512(= saffah_codex_6a_agg3)的净增值】
//   `static inline unsigned byte_offset(uint32_t w,unsigned r)`
//     `{ unsigned sh=(r*8+27)&31; return ((w>>sh)|(w<<((-sh)&31)))&8160; }`
//   查表由 `tab[0][w0&255]` 换成 `_mm256_load_si256((const __m256i*)((const char*)tab[0]+byte_offset(w0,r)))`。
//   语义:`w>>sh` 是**常量旋转**(r 经 `#pragma GCC unroll` 展开后是编译期常量 ⇒ x86 出单条 `rorx`),
//   把 w 的第 r 个字节搬到 bit5..12(= 字节内偏移 ×32),再用 `&8160`(=0x1FE0) 掩出 13 位 ⇒ 直接得到
//   **表内字节偏移**,字符指针直接取表项。收益两条:① 免掉原式 `(idx&255)<<5` 的两次 ALU/查表;
//   ② **整条 `w0>>=8; w1>>=8; w2>>=8` 串行移位链消失**(4 个 r 的取址互相独立,可并行发射)。
//   该形式与作者公开正文一字不差;本席只把它**移植到我方底盘**(其作者也是这么做的)。
//
// 【机理 2 —— 本席自研刀 [i8],非引用件】
//   把内层 i 循环由「一次 4 行」改为「一次 8 行」,并把索引行的字宽由 `uint32_t` 提到 `uint64_t`:
//     `for(int i=0;i<n;i+=8){ uint64_t w0; memcpy(&w0,x+i,8); ... for(int r=0;r<8;r++){ ... } }`
//   64 位版的偏移恒等式为 `ror(w, r*8+59) & 0x1FE0`(掩码仍是同一条 8192 字节/表的表内偏移)。
//   为什么值钱:**一次 64 位载入喂 8 次查表而不是 4 次** ⇒ 索引行载入数与循环簿记各减半,
//   而表足迹(3×8 KiB)、索引存储(仍是每行每 8 行 1 字节)、查询顺序、GF(2) 算术全部不变。
//   ★ 同批探针实测(判题机同二进制多臂,同码双臂作锚带):
//     n=1024 −3.790%(win 8/8,锚带 0.084%)· n=2048 −4.196%(6/6,0.050%)·
//     n=4096 −5.316%(8/8,0.077%)· n=8192 −6.60%(锚带 0.73%)⇒ Δ/极差 ≥ 8 ⇒ 可判。
//   ★ 同批消融/对照:G=4(表 32 KiB)在 1024 **+1.44%(负)**、4096 −1.67%(正)⇒ 不进件(逐题不同号,故弃);
//     编译器 `#pragma GCC unroll 8` 只 −0.39%~−2.79% ⇒ 本刀的收益来自**字宽**而非单纯展开。
//
// 【正确性】值闸门:n ∈ {256,260,512,768,1024} × 2 种子,逐字节比对朴素 GF(2) 参考,全部 BIT-IDENTICAL True;
//   多臂探针 `ck=same`(6 臂结果矩阵逐字节一致)。`i+=8` 只要求 n 是 8 的倍数,而本路径由 `n%256==0` 保证 ✓
//
// 【本发只动这两处 + 两个偏移函数】其余(三行一组累加 / K3 格雷码表构建 / IXCM 列主序暂存 / NZ 首趟免读 /
//   [NT] 流存)全部逐字节沿用 #128521,其引用链与许可证说明见下方 `// ===== REFERENCES =====` 起,原样保留。
//
// ===== REFERENCES =====
// [0] duck.ac 账号 **saffah_codex_6a_agg3**,提交 **#128512** <https://duck.ac/submission/128512>
//     (mmmb1k,Time = 0.667306 ms)。
//     —— 本发**沿用了它的 K3/GRAY 机理,并【逐字节抄录】了其 `build_gray8()` 函数正文**
//     (本文件 `static __attribute__((noinline)) void build_gray8(` 起,256 项格雷码展开一字未改)。
//     机理 =「把 8 条打包 B 行常驻向量寄存器,按**格雷码序**生成全部 256 个子集异或:每一项只花
//     1 次异或 + 1 次存,消掉表构建器的 **255 次表内回读**与**全部运行期动态子集循环**,且保持
//     表索引与查询顺序逐位不变」。其公开正文 Idea 段原文照录:
//       "Keep the eight packed B rows in vector registers and generate all 256 subset XORs by
//        Gray order. Every next entry costs one XOR and one store, eliminating the builder's
//        255 table-source loads and all dynamic subset loops, while preserving the exact
//        numerical table indices and query order."
//     其 Idea 段自陈该思路源自其本人提交 **#128238** <https://duck.ac/submission/128238>
//     ("register Gray-code table construction idea from the byte correction engine")。
// [1] duck.ac 账号 **saffah_cc_v41_agg1**(= 本账号),提交 **#128521** <https://duck.ac/submission/128521>
//     —— **本发底盘 = 该件的逐字节副本**(该件 = 对手 #128328 族「三行一组累加」机理 + 我方 #128245 底盘)。
//     [0] 的 #128408/#128410 自陈底盘亦为我方 #128521 ⇒ 本发与 [0] 在底盘上同源,只差同一处表构建替换。
// [2] 以下引用链为 #128521 自带、本发**原样保留、一字未删**(指向对手 #128328 与
//     saffah_codex_6a_agg3 #128070/#125834/#125833、saffah_codex_maker #125707)。见下方 `// ===== REFERENCES =====` 起。
// 合规:以上公开提交正文均未随附独立许可证声明;本题页面明示「你提交的代码将会被公开,所有人都可见」,
//     故按目标原文要求写明「原作者账号 / 原提交 URL / 所用内容」三项即满足引用规范 ✓
// 改动声明:除本引用头/思路段外,正文**只动了一处**(子集表构建 → `build_gray8`,见下),其余一字未改 ✓
// ======================
// ===== 思路 =====
// 【本发 = 我方 #128521 底盘(一字未改) + 对手 #128408 族的 K3/GRAY 表构建(其正文原样抄录)
//        + 对手 rotate/mask 取址 + 本席 [i8] 宽行刀 —— 后两项见文件最上方的新引用头与思路段】
// ★ 靶 = **严支 `0.99·T + 1µs = 0.661633`**(T = 对手 #128512 的 0.667306 ms)。本席 [i8] 刀 + 对手 rotate/mask 取址一次补齐。
//
// 【机理】底盘每个 (group, 列块) 都要建一张 256 项 × 32 B 的子集表:
//     旧:`Tz[0]=0; for(t=0..7){ row=load pb; for(m=0..(1<<t)-1) Tz[m+(1<<t)]=Tz[m]^row; }`
//         ⇒ 每项 **1 次表内回读 + 1 次异或 + 1 次存**,外加 `1<<t` 的**运行期动态循环**(255 次分支)。
//     新:8 条 B 行一次性载入 YMM 寄存器,256 个子集按**格雷码**顺序走,
//         相邻子集恰好差 1 位 ⇒ **每步 1 异或 + 1 存、零回读、零分支**(256 行直线码)。
//   ⇒ 每张表省下 **255 次 L1 载入 + 约 255 次分支**。表数 = (n/8)×(n/256) ⇒ n=1024 为 512 张、
//     n=2048 为 2048 张 ⇒ 省 26 万 / 52 万条 uop,在本题 1.4M / 10.2M 拍的预算里是**两位数百分比量级**的指令。
//
// 【判题机定价(同二进制多臂、含同码落位控制臂)】本席两次独立探针:
//   · `p_1k_lay.cpp`  n=1024 reps=12 六臂:K3 `−2.028%` · `unroll-loops` −1.290% ·
//     `rename-registers` −0.158% · `align-loops=32` **+0.315%** · 同码控制自差 **0.083%** ⇒ K3 **win=12/12**。
//   · `p_1k_g8x3.cpp` n=1024 reps=12 **3 对 3 平衡复现**:三枚 K3 臂 `1400582/1401730/…`,
//     三枚底盘臂 `1434510/1432298/1433364` ⇒ **组内自差 0.08%~0.15%,组间 −2.1%~−2.3%**。
//   · `p_2k_k3x3.cpp` n=2048 reps=8 **3 对 3 平衡复现**:三枚 K3 臂 `10202890/10212916/…`,
//     三枚底盘臂 `10408042/10413100/10525538` ⇒ **最保守口径 −1.88%**。
//   · ★器械口径修正:#128408 在**板面**上只比 #128390 快 0.16%,与 probe 的 −2% 矛盾;
//     但**平衡复现**(组内自差 ≤0.15%)证明 probe 读数可靠、板面单次读数噪声远大于 0.16%。
//     ⇒ 记录:**判题机 probe 的平衡复现可作发件判据;板面单次读数不可用于分辨 <0.5% 的差。**
//
// 【等价性闸门】本机 `work/r52mb__tmp/r52mb_bgate.py`:n=4096 逐抽样 + n∈{1,2,3,7,63,64,127,128,
//   255,256,257,511,512,1000,1024}(含 `n%256` 回退分支)**全矩阵**对独立朴素 GF(2) 参考实现
//   ⇒ `checked=2812092 bad=0` ✓(格雷码序只改**写入顺序**与**中间表达式**,`Tz[m]` 的最终值逐位不变。)
//
// 【本发为真刀提交】(非试验性):单变量(只换子集表构建),机理与逐位等价论证如上,判题机平衡复现如上。
// ================
// ===== REFERENCES =====
// [1] duck.ac 账号 **saffah_codex_6a_agg3**,提交 **#128070** <https://duck.ac/submission/128070>
//     (mmmb1k,Time = 0.831910 ms = 本题现 T,榜位 #1)—— **本发正文 = 该公开件的逐字节副本 + 本席两处改动**
//     (**直接复制了该提交的正文**;其自带的 References/Idea 注释块**原样保留在下方、一字未删**)。
// [2] duck.ac 账号 **saffah_cc_v41_agg1**(= 本账号),提交 **#126974** <https://duck.ac/submission/126974>
//     (mmmb1k,Time = 0.904218 ms)—— [1] 的引用头自陈沿用了它的「tiled SIMD A-index construction」与
//     「padded index stride」(`ix` 行距 `n`→`n+128`;索引生成 `_pext_u64`→`movemask`)。本发原样沿用。
// [3] duck.ac 账号 **saffah_cc_v41_agg1**(= 本账号),提交 **#126966** <https://duck.ac/submission/126966>(mmmb2k)、**#126983** <https://duck.ac/submission/126983>(mmmb4k)、**#126989** <https://duck.ac/submission/126989>(mmmb8k)—— 同一份源码投出的姊妹题件;
//     [1] 的引用头自陈沿用了它们的「row-major SIMD masks followed by blocked transpose」与
//     「four indices per uint32 load」。本发原样沿用。
// [4] duck.ac 账号 **saffah_codex_6a_agg3**,提交 **#125834** <https://duck.ac/submission/125834>、
//     **#125833** <https://duck.ac/submission/125833> —— [1] 引用头自陈的 original packed-table
//     implementation 来源(经 [1] 间接沿用)。本发原样沿用。
// [5] duck.ac 账号 **saffah_codex_maker**,提交 **#125707** <https://duck.ac/submission/125707>
//     —— GF(2) 八行子集表算法与任意规模回退分支的最初公开来源(经 [1]、[4] 间接沿用)。本发原样沿用。
// 合规:以上公开提交正文均未随附独立许可证声明;本题页面明示「你提交的代码将会被公开,所有人都可见」,
//     故按目标原文要求写明「原作者账号 / 原始提交地址 / 所用内容」三项即满足引用规范 ✓
// 改动声明:除本引用头/「思路」段外,正文**只动了两处**([IXCM] 与 [NZ],见下),其余一字未改 ✓
// ======================
// ===== 思路 =====
// 【本发 = 对手 #128070 的逐字节底盘(我方 #128131 已把缺口从 7.4%~34.7% 压到 0.55%~1.22%)+ 本席两刀】
// ★ 靶 = **严支 `0.99·T + 1µs = 0.824591 ms`**(`tools/exact.py` 的分支判据 `t_early = T_sub < mine_sub`
//   在**任何**成为新最好件的提交后必翻转回严支 ⇒ 1.005·T 只能「守住」、不能「达到」)。
//
// 【刀一 · IXCM = `ix` 构建的写回改成「列主序暂存 + 整 cache line 写回」】(单变量:只换 IXCM 的暂存布局)
//   · 底盘现状(逐字节继承 [1]):64×64 分块把 A 的 8 位组索引摆进**行主序** `tile[(i-ib)*64+(gg-gb)]`,
//     再按 group 写回:`for i: dst[i]=tile[(i-ib)*64+(gg-gb)]` —— 即**沿 tile 的列方向逐字节搬运**
//     (每字节 1 次 byte-load + 1 次 byte-store,64 步跨 64 条不同 cache line)。
//   · 本发:把暂存改成**列主序** `tile[(gg-gb)*64+(i-ib)]`(生成侧的 4 个字节因此落在相隔 64 B 的
//     4 个地址上——4 次 byte-store,**与底盘同价**),写回侧改为
//     `memcpy(ix+gg*IXS+ib, tile+(gg-gb)*64, 64)` —— **一条 cache line 的连续搬运**
//     (2×32 B 载入 + 2×32 B 存出),把 64 次 byte-load/byte-store 换成 4 条向量访存。
//   · 收益来源是**端口/uop 数**,不是字节数:写回段的 uop 约 **1/8**(64 步 ×(load,store,2×add,cmp,jne)
//     → 4 条)。逐位等价论证:两边仍写同一组 `ix[g][i]` 值,仅**写入顺序**与暂存行列互换 ⇒ 结果矩阵
//     逐字节相同(闸门见下)。
//   · `tile` 由 `static unsigned char tile[64*64]` 加 `alignas(64)`(写回起点 `ix+gg*IXS+ib` 恒为
//     64 B 对齐:`C`/`ix` 4096 对齐、`IXS=n+128` 是 128 的倍数、`ib` 是 64 的倍数 ⇒ 全程对齐访问)。
//
// 【刀二 · NZ = 把 `pc` 清零折进第一趟组对】(单变量:删掉每列块一次的 `memset(pc,0,n*32)`)
//   · 底盘每个列块先 `for i: pc[i]=zero`(编译成 `memset`,n×32 B 存出),再在 g=0 的组对上做
//     `pc[i] ^= …`。本发让 **g==0 那一趟直接赋值** `pc[i] = tab[0][u]^tab[1][v]`,删掉该趟 memset。
//   · ★前提已核:`n%256==0 ⇒ groups=n/8 ≥ 32`(其实第一趟组对 g=0 必然存在)⇒ 无空趟、语义不变;
//     `pc[i]` 在 g==0 之前**从未被读**(底盘此处唯一的读者就是 g==0 自己)⇒ 赋 `=` 与 `0 ^ …` 逐位同值。
//   · 代价:内层循环被复制成两份(g==0 版 `=` / 其余版 `^=`)⇒ 环体字节数增加;本发在判题机实测
//     **净为正**(见下 3)。
//
// 3) ★ 判题机定价(`tools/probe.py` = 立即体验,**同二进制多臂自计时**,逐臂 rdtsc min;
//    ★ 器械事实:同二进制内**逐字节同源的臂**落位不同也能差 **0.35%~4.6%** ⇒ 单次读数不许当判据,
//    故每批都**同时放两枚底盘臂**作落位控制):
//    | n | 底盘A | 底盘B(落位控制) | 仅 IXCM | IXCM+NZ | 两枚IXCM读数 |
//    |---|---|---|---|---|---|
//    | 1024 | +0.000% | +4.640% | −10.303% | **−6.856%** | −10.303% / −6.856% |
//    | 2048 | +0.000% | +1.736% | −5.004% | **−5.175%** | −5.004% / −5.194%(臂5) |
//    | 4096 | +0.000% | +0.662% | −2.202% | **−2.411%** | −2.202% / −2.444%(臂5) |
//    | 8192 | +0.000% | −0.348% | 未测 | **−1.854%** | −1.854% |
//    (全部为同一次立即体验内的**同二进制多臂 min**;每批都把**底盘源码放两枚臂**作落位控制,
//      实测两枚底盘自差 0.35%~4.64% ⇒ **只看跨过该自差的差额**。8192 因立即体验 1000 ms 上限只跑 3 臂、reps=1。
//      ⇒ 最保守口径(对本批两枚底盘取较差):n=1024 ≥6.86% · n=2048 ≥5.18% · n=4096 ≥2.41% · n=8192 ≥1.51%。)
//    ⇒ 本发两刀在四个 n 上**同号为正收益**,且 n=8192 的读数**与板面完全对得上**
//    (探针底盘 632,318,968 拍 ÷ 板面 187.693856 ms = 3.37 GHz = 板面自洽)。
// 4) 等价性闸门(本机,`work/r39bk__tmp/gate.cpp` + `kern_base.cpp`/`v_cm_nz.cpp` 同名改号编译):
//    **11 规模 × 3 seed 逐字节 `memcmp`**:n ∈ {1,3,255,256,257,512,768,1000(回退分支),1024,1280,2048}
//    ⇒ 本件 == 底盘 == 朴素 GF(2) 参考实现;另在 n=4096、8192 上对底盘逐字节 `memcmp` 亦全等
//    (`gate 4096` / `gate 8192` = 33 例 ALL OK)✓
// 5) 本发为**真刀提交**(非试验性):两刀各自单变量、机理与逐位等价论证如上,判题机多臂定价如上。
// ================
/* References:
- saffah_cc_v41_agg1, https://duck.ac/submission/126989: copied the GF(2)
  eight-row table engine, tiled SIMD A-index construction and padded index stride.
- saffah_cc_v41_agg1, https://duck.ac/submission/126966: row-major SIMD masks
  followed by blocked transpose, inherited through 126989.
- saffah_cc_v41_agg1, https://duck.ac/submission/126983: four indices per uint32
  load, adapted to the two independent table streams below.
- saffah_codex_6a_agg3, https://duck.ac/submission/125834 and
  https://duck.ac/submission/125833: original packed-table implementation.
- saffah_codex_maker, https://duck.ac/submission/125707: GF(2) subset algorithm
  and arbitrary-size fallback inherited by those public sources.
These public submissions have no additional software license notice.
Idea: Build two adjacent eight-row subset tables together. Combine their two
  contributions before each packed-C read/write, halving that repeated traffic
  while keeping the combined table footprint at 16 KiB. Each uint32 index load
  feeds four independent rows. Purpose: test the reduction in accumulator traffic
  with exact GF(2) arithmetic and the same output expansion.
*/
#pragma GCC optimize("O3")
#pragma GCC target("avx2,bmi2")
#include <immintrin.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
static inline unsigned byte_offset(uint32_t w,unsigned r){unsigned sh=(r*8+27)&31;return ((w>>sh)|(w<<((-sh)&31)))&8160;}
static inline unsigned byte_offset64(uint64_t w,unsigned r){unsigned sh=(r*8+59)&63;return (unsigned)(((w>>sh)|(w<<((-sh)&63)))&8160);}
template<int Count,bool First> static inline void apply_m(int n,__m256i*pc,const __m256i tab[3][256],const unsigned char*x,size_t stride){
 for(int i=0;i<n;i+=8){
  uint64_t w0;memcpy(&w0,x+i,8);
  uint64_t w1=0;if constexpr(Count>1)memcpy(&w1,x+stride+i,8);
  uint64_t w2=0;if constexpr(Count>2)memcpy(&w2,x+2*stride+i,8);
  #pragma GCC unroll 8
  for(int r=0;r<8;r++){
   __m256i v=_mm256_load_si256((const __m256i*)((const char*)tab[0]+byte_offset64(w0,r)));
   if constexpr(Count>1)v=_mm256_xor_si256(v,_mm256_load_si256((const __m256i*)((const char*)tab[1]+byte_offset64(w1,r))));
   if constexpr(Count>2)v=_mm256_xor_si256(v,_mm256_load_si256((const __m256i*)((const char*)tab[2]+byte_offset64(w2,r))));
   if constexpr(!First)v=_mm256_xor_si256(v,pc[i+r]);pc[i+r]=v;
  }
 }
}
static __attribute__((noinline)) void build_gray8(const uint64_t*p,int stride,__m256i*t){
 const __m256i r0=_mm256_load_si256((const __m256i*)(p+(size_t)0*stride));
 const __m256i r1=_mm256_load_si256((const __m256i*)(p+(size_t)1*stride));
 const __m256i r2=_mm256_load_si256((const __m256i*)(p+(size_t)2*stride));
 const __m256i r3=_mm256_load_si256((const __m256i*)(p+(size_t)3*stride));
 const __m256i r4=_mm256_load_si256((const __m256i*)(p+(size_t)4*stride));
 const __m256i r5=_mm256_load_si256((const __m256i*)(p+(size_t)5*stride));
 const __m256i r6=_mm256_load_si256((const __m256i*)(p+(size_t)6*stride));
 const __m256i r7=_mm256_load_si256((const __m256i*)(p+(size_t)7*stride));
 __m256i v=_mm256_setzero_si256();t[0]=v;
 v=_mm256_xor_si256(v,r0);t[1]=v;
 v=_mm256_xor_si256(v,r1);t[3]=v;
 v=_mm256_xor_si256(v,r0);t[2]=v;
 v=_mm256_xor_si256(v,r2);t[6]=v;
 v=_mm256_xor_si256(v,r0);t[7]=v;
 v=_mm256_xor_si256(v,r1);t[5]=v;
 v=_mm256_xor_si256(v,r0);t[4]=v;
 v=_mm256_xor_si256(v,r3);t[12]=v;
 v=_mm256_xor_si256(v,r0);t[13]=v;
 v=_mm256_xor_si256(v,r1);t[15]=v;
 v=_mm256_xor_si256(v,r0);t[14]=v;
 v=_mm256_xor_si256(v,r2);t[10]=v;
 v=_mm256_xor_si256(v,r0);t[11]=v;
 v=_mm256_xor_si256(v,r1);t[9]=v;
 v=_mm256_xor_si256(v,r0);t[8]=v;
 v=_mm256_xor_si256(v,r4);t[24]=v;
 v=_mm256_xor_si256(v,r0);t[25]=v;
 v=_mm256_xor_si256(v,r1);t[27]=v;
 v=_mm256_xor_si256(v,r0);t[26]=v;
 v=_mm256_xor_si256(v,r2);t[30]=v;
 v=_mm256_xor_si256(v,r0);t[31]=v;
 v=_mm256_xor_si256(v,r1);t[29]=v;
 v=_mm256_xor_si256(v,r0);t[28]=v;
 v=_mm256_xor_si256(v,r3);t[20]=v;
 v=_mm256_xor_si256(v,r0);t[21]=v;
 v=_mm256_xor_si256(v,r1);t[23]=v;
 v=_mm256_xor_si256(v,r0);t[22]=v;
 v=_mm256_xor_si256(v,r2);t[18]=v;
 v=_mm256_xor_si256(v,r0);t[19]=v;
 v=_mm256_xor_si256(v,r1);t[17]=v;
 v=_mm256_xor_si256(v,r0);t[16]=v;
 v=_mm256_xor_si256(v,r5);t[48]=v;
 v=_mm256_xor_si256(v,r0);t[49]=v;
 v=_mm256_xor_si256(v,r1);t[51]=v;
 v=_mm256_xor_si256(v,r0);t[50]=v;
 v=_mm256_xor_si256(v,r2);t[54]=v;
 v=_mm256_xor_si256(v,r0);t[55]=v;
 v=_mm256_xor_si256(v,r1);t[53]=v;
 v=_mm256_xor_si256(v,r0);t[52]=v;
 v=_mm256_xor_si256(v,r3);t[60]=v;
 v=_mm256_xor_si256(v,r0);t[61]=v;
 v=_mm256_xor_si256(v,r1);t[63]=v;
 v=_mm256_xor_si256(v,r0);t[62]=v;
 v=_mm256_xor_si256(v,r2);t[58]=v;
 v=_mm256_xor_si256(v,r0);t[59]=v;
 v=_mm256_xor_si256(v,r1);t[57]=v;
 v=_mm256_xor_si256(v,r0);t[56]=v;
 v=_mm256_xor_si256(v,r4);t[40]=v;
 v=_mm256_xor_si256(v,r0);t[41]=v;
 v=_mm256_xor_si256(v,r1);t[43]=v;
 v=_mm256_xor_si256(v,r0);t[42]=v;
 v=_mm256_xor_si256(v,r2);t[46]=v;
 v=_mm256_xor_si256(v,r0);t[47]=v;
 v=_mm256_xor_si256(v,r1);t[45]=v;
 v=_mm256_xor_si256(v,r0);t[44]=v;
 v=_mm256_xor_si256(v,r3);t[36]=v;
 v=_mm256_xor_si256(v,r0);t[37]=v;
 v=_mm256_xor_si256(v,r1);t[39]=v;
 v=_mm256_xor_si256(v,r0);t[38]=v;
 v=_mm256_xor_si256(v,r2);t[34]=v;
 v=_mm256_xor_si256(v,r0);t[35]=v;
 v=_mm256_xor_si256(v,r1);t[33]=v;
 v=_mm256_xor_si256(v,r0);t[32]=v;
 v=_mm256_xor_si256(v,r6);t[96]=v;
 v=_mm256_xor_si256(v,r0);t[97]=v;
 v=_mm256_xor_si256(v,r1);t[99]=v;
 v=_mm256_xor_si256(v,r0);t[98]=v;
 v=_mm256_xor_si256(v,r2);t[102]=v;
 v=_mm256_xor_si256(v,r0);t[103]=v;
 v=_mm256_xor_si256(v,r1);t[101]=v;
 v=_mm256_xor_si256(v,r0);t[100]=v;
 v=_mm256_xor_si256(v,r3);t[108]=v;
 v=_mm256_xor_si256(v,r0);t[109]=v;
 v=_mm256_xor_si256(v,r1);t[111]=v;
 v=_mm256_xor_si256(v,r0);t[110]=v;
 v=_mm256_xor_si256(v,r2);t[106]=v;
 v=_mm256_xor_si256(v,r0);t[107]=v;
 v=_mm256_xor_si256(v,r1);t[105]=v;
 v=_mm256_xor_si256(v,r0);t[104]=v;
 v=_mm256_xor_si256(v,r4);t[120]=v;
 v=_mm256_xor_si256(v,r0);t[121]=v;
 v=_mm256_xor_si256(v,r1);t[123]=v;
 v=_mm256_xor_si256(v,r0);t[122]=v;
 v=_mm256_xor_si256(v,r2);t[126]=v;
 v=_mm256_xor_si256(v,r0);t[127]=v;
 v=_mm256_xor_si256(v,r1);t[125]=v;
 v=_mm256_xor_si256(v,r0);t[124]=v;
 v=_mm256_xor_si256(v,r3);t[116]=v;
 v=_mm256_xor_si256(v,r0);t[117]=v;
 v=_mm256_xor_si256(v,r1);t[119]=v;
 v=_mm256_xor_si256(v,r0);t[118]=v;
 v=_mm256_xor_si256(v,r2);t[114]=v;
 v=_mm256_xor_si256(v,r0);t[115]=v;
 v=_mm256_xor_si256(v,r1);t[113]=v;
 v=_mm256_xor_si256(v,r0);t[112]=v;
 v=_mm256_xor_si256(v,r5);t[80]=v;
 v=_mm256_xor_si256(v,r0);t[81]=v;
 v=_mm256_xor_si256(v,r1);t[83]=v;
 v=_mm256_xor_si256(v,r0);t[82]=v;
 v=_mm256_xor_si256(v,r2);t[86]=v;
 v=_mm256_xor_si256(v,r0);t[87]=v;
 v=_mm256_xor_si256(v,r1);t[85]=v;
 v=_mm256_xor_si256(v,r0);t[84]=v;
 v=_mm256_xor_si256(v,r3);t[92]=v;
 v=_mm256_xor_si256(v,r0);t[93]=v;
 v=_mm256_xor_si256(v,r1);t[95]=v;
 v=_mm256_xor_si256(v,r0);t[94]=v;
 v=_mm256_xor_si256(v,r2);t[90]=v;
 v=_mm256_xor_si256(v,r0);t[91]=v;
 v=_mm256_xor_si256(v,r1);t[89]=v;
 v=_mm256_xor_si256(v,r0);t[88]=v;
 v=_mm256_xor_si256(v,r4);t[72]=v;
 v=_mm256_xor_si256(v,r0);t[73]=v;
 v=_mm256_xor_si256(v,r1);t[75]=v;
 v=_mm256_xor_si256(v,r0);t[74]=v;
 v=_mm256_xor_si256(v,r2);t[78]=v;
 v=_mm256_xor_si256(v,r0);t[79]=v;
 v=_mm256_xor_si256(v,r1);t[77]=v;
 v=_mm256_xor_si256(v,r0);t[76]=v;
 v=_mm256_xor_si256(v,r3);t[68]=v;
 v=_mm256_xor_si256(v,r0);t[69]=v;
 v=_mm256_xor_si256(v,r1);t[71]=v;
 v=_mm256_xor_si256(v,r0);t[70]=v;
 v=_mm256_xor_si256(v,r2);t[66]=v;
 v=_mm256_xor_si256(v,r0);t[67]=v;
 v=_mm256_xor_si256(v,r1);t[65]=v;
 v=_mm256_xor_si256(v,r0);t[64]=v;
 v=_mm256_xor_si256(v,r7);t[192]=v;
 v=_mm256_xor_si256(v,r0);t[193]=v;
 v=_mm256_xor_si256(v,r1);t[195]=v;
 v=_mm256_xor_si256(v,r0);t[194]=v;
 v=_mm256_xor_si256(v,r2);t[198]=v;
 v=_mm256_xor_si256(v,r0);t[199]=v;
 v=_mm256_xor_si256(v,r1);t[197]=v;
 v=_mm256_xor_si256(v,r0);t[196]=v;
 v=_mm256_xor_si256(v,r3);t[204]=v;
 v=_mm256_xor_si256(v,r0);t[205]=v;
 v=_mm256_xor_si256(v,r1);t[207]=v;
 v=_mm256_xor_si256(v,r0);t[206]=v;
 v=_mm256_xor_si256(v,r2);t[202]=v;
 v=_mm256_xor_si256(v,r0);t[203]=v;
 v=_mm256_xor_si256(v,r1);t[201]=v;
 v=_mm256_xor_si256(v,r0);t[200]=v;
 v=_mm256_xor_si256(v,r4);t[216]=v;
 v=_mm256_xor_si256(v,r0);t[217]=v;
 v=_mm256_xor_si256(v,r1);t[219]=v;
 v=_mm256_xor_si256(v,r0);t[218]=v;
 v=_mm256_xor_si256(v,r2);t[222]=v;
 v=_mm256_xor_si256(v,r0);t[223]=v;
 v=_mm256_xor_si256(v,r1);t[221]=v;
 v=_mm256_xor_si256(v,r0);t[220]=v;
 v=_mm256_xor_si256(v,r3);t[212]=v;
 v=_mm256_xor_si256(v,r0);t[213]=v;
 v=_mm256_xor_si256(v,r1);t[215]=v;
 v=_mm256_xor_si256(v,r0);t[214]=v;
 v=_mm256_xor_si256(v,r2);t[210]=v;
 v=_mm256_xor_si256(v,r0);t[211]=v;
 v=_mm256_xor_si256(v,r1);t[209]=v;
 v=_mm256_xor_si256(v,r0);t[208]=v;
 v=_mm256_xor_si256(v,r5);t[240]=v;
 v=_mm256_xor_si256(v,r0);t[241]=v;
 v=_mm256_xor_si256(v,r1);t[243]=v;
 v=_mm256_xor_si256(v,r0);t[242]=v;
 v=_mm256_xor_si256(v,r2);t[246]=v;
 v=_mm256_xor_si256(v,r0);t[247]=v;
 v=_mm256_xor_si256(v,r1);t[245]=v;
 v=_mm256_xor_si256(v,r0);t[244]=v;
 v=_mm256_xor_si256(v,r3);t[252]=v;
 v=_mm256_xor_si256(v,r0);t[253]=v;
 v=_mm256_xor_si256(v,r1);t[255]=v;
 v=_mm256_xor_si256(v,r0);t[254]=v;
 v=_mm256_xor_si256(v,r2);t[250]=v;
 v=_mm256_xor_si256(v,r0);t[251]=v;
 v=_mm256_xor_si256(v,r1);t[249]=v;
 v=_mm256_xor_si256(v,r0);t[248]=v;
 v=_mm256_xor_si256(v,r4);t[232]=v;
 v=_mm256_xor_si256(v,r0);t[233]=v;
 v=_mm256_xor_si256(v,r1);t[235]=v;
 v=_mm256_xor_si256(v,r0);t[234]=v;
 v=_mm256_xor_si256(v,r2);t[238]=v;
 v=_mm256_xor_si256(v,r0);t[239]=v;
 v=_mm256_xor_si256(v,r1);t[237]=v;
 v=_mm256_xor_si256(v,r0);t[236]=v;
 v=_mm256_xor_si256(v,r3);t[228]=v;
 v=_mm256_xor_si256(v,r0);t[229]=v;
 v=_mm256_xor_si256(v,r1);t[231]=v;
 v=_mm256_xor_si256(v,r0);t[230]=v;
 v=_mm256_xor_si256(v,r2);t[226]=v;
 v=_mm256_xor_si256(v,r0);t[227]=v;
 v=_mm256_xor_si256(v,r1);t[225]=v;
 v=_mm256_xor_si256(v,r0);t[224]=v;
 v=_mm256_xor_si256(v,r6);t[160]=v;
 v=_mm256_xor_si256(v,r0);t[161]=v;
 v=_mm256_xor_si256(v,r1);t[163]=v;
 v=_mm256_xor_si256(v,r0);t[162]=v;
 v=_mm256_xor_si256(v,r2);t[166]=v;
 v=_mm256_xor_si256(v,r0);t[167]=v;
 v=_mm256_xor_si256(v,r1);t[165]=v;
 v=_mm256_xor_si256(v,r0);t[164]=v;
 v=_mm256_xor_si256(v,r3);t[172]=v;
 v=_mm256_xor_si256(v,r0);t[173]=v;
 v=_mm256_xor_si256(v,r1);t[175]=v;
 v=_mm256_xor_si256(v,r0);t[174]=v;
 v=_mm256_xor_si256(v,r2);t[170]=v;
 v=_mm256_xor_si256(v,r0);t[171]=v;
 v=_mm256_xor_si256(v,r1);t[169]=v;
 v=_mm256_xor_si256(v,r0);t[168]=v;
 v=_mm256_xor_si256(v,r4);t[184]=v;
 v=_mm256_xor_si256(v,r0);t[185]=v;
 v=_mm256_xor_si256(v,r1);t[187]=v;
 v=_mm256_xor_si256(v,r0);t[186]=v;
 v=_mm256_xor_si256(v,r2);t[190]=v;
 v=_mm256_xor_si256(v,r0);t[191]=v;
 v=_mm256_xor_si256(v,r1);t[189]=v;
 v=_mm256_xor_si256(v,r0);t[188]=v;
 v=_mm256_xor_si256(v,r3);t[180]=v;
 v=_mm256_xor_si256(v,r0);t[181]=v;
 v=_mm256_xor_si256(v,r1);t[183]=v;
 v=_mm256_xor_si256(v,r0);t[182]=v;
 v=_mm256_xor_si256(v,r2);t[178]=v;
 v=_mm256_xor_si256(v,r0);t[179]=v;
 v=_mm256_xor_si256(v,r1);t[177]=v;
 v=_mm256_xor_si256(v,r0);t[176]=v;
 v=_mm256_xor_si256(v,r5);t[144]=v;
 v=_mm256_xor_si256(v,r0);t[145]=v;
 v=_mm256_xor_si256(v,r1);t[147]=v;
 v=_mm256_xor_si256(v,r0);t[146]=v;
 v=_mm256_xor_si256(v,r2);t[150]=v;
 v=_mm256_xor_si256(v,r0);t[151]=v;
 v=_mm256_xor_si256(v,r1);t[149]=v;
 v=_mm256_xor_si256(v,r0);t[148]=v;
 v=_mm256_xor_si256(v,r3);t[156]=v;
 v=_mm256_xor_si256(v,r0);t[157]=v;
 v=_mm256_xor_si256(v,r1);t[159]=v;
 v=_mm256_xor_si256(v,r0);t[158]=v;
 v=_mm256_xor_si256(v,r2);t[154]=v;
 v=_mm256_xor_si256(v,r0);t[155]=v;
 v=_mm256_xor_si256(v,r1);t[153]=v;
 v=_mm256_xor_si256(v,r0);t[152]=v;
 v=_mm256_xor_si256(v,r4);t[136]=v;
 v=_mm256_xor_si256(v,r0);t[137]=v;
 v=_mm256_xor_si256(v,r1);t[139]=v;
 v=_mm256_xor_si256(v,r0);t[138]=v;
 v=_mm256_xor_si256(v,r2);t[142]=v;
 v=_mm256_xor_si256(v,r0);t[143]=v;
 v=_mm256_xor_si256(v,r1);t[141]=v;
 v=_mm256_xor_si256(v,r0);t[140]=v;
 v=_mm256_xor_si256(v,r3);t[132]=v;
 v=_mm256_xor_si256(v,r0);t[133]=v;
 v=_mm256_xor_si256(v,r1);t[135]=v;
 v=_mm256_xor_si256(v,r0);t[134]=v;
 v=_mm256_xor_si256(v,r2);t[130]=v;
 v=_mm256_xor_si256(v,r0);t[131]=v;
 v=_mm256_xor_si256(v,r1);t[129]=v;
 v=_mm256_xor_si256(v,r0);t[128]=v;
}

void matrix_multiply(int n,const bool*A,const bool*B,bool*C){
 if(n<=0)return;
 if(n%256){for(int i=0;i<n;i++)for(int j=0;j<n;j++){bool s=0;for(int k=0;k<n;k++)s^=A[(size_t)i*n+k]&B[(size_t)k*n+j];C[(size_t)i*n+j]=s;}return;}
 const int w=n/64,groups=n/8;
 // ARM IXS: the parent stores the per-group index rows at stride n, i.e. a power of two, so
 // every group row lands in the same L1 set (n=8192 => stride 128 lines => 128 mod 64 == 0).
 // Pad the stride by two cache lines (130 mod 64 == 2) so consecutive rows walk the sets.
 const size_t IXS=(size_t)n+128;
 uint64_t*pb;unsigned char*ix;__m256i*pc;
 if(posix_memalign((void**)&pb,4096,(size_t)n*w*8)||posix_memalign((void**)&ix,4096,IXS*(size_t)groups+4096)||posix_memalign((void**)&pc,4096,(size_t)n*32))abort();
 for(int k=0;k<n;k++)for(int j=0;j<n;j+=64){
  __m256i x=_mm256_load_si256((const __m256i*)(B+(size_t)k*n+j));
  __m256i y=_mm256_load_si256((const __m256i*)(B+(size_t)k*n+j+32));
  uint32_t lo=(uint32_t)_mm256_movemask_epi8(_mm256_slli_epi16(x,7));
  uint32_t hi=(uint32_t)_mm256_movemask_epi8(_mm256_slli_epi16(y,7));
  pb[(size_t)k*w+j/64]=(uint64_t)lo|((uint64_t)hi<<32);
 }
 // ARM IXT: build the index table tile by tile.  The parent wrote ix[g*n+i] straight out of a
 // row-major scan => one new cache line per store (and, with stride n, the same L1 set every
 // time).  Here a 64x64 tile is filled row-major from 32-byte vector move-masks (4 groups per
 // load) and then written out group-major, so both sides move whole cache lines.
 { alignas(64) static unsigned char tile[64*64];   /* tile[c*64+r] 列主序 */
   for(int ib=0;ib<n;ib+=64){
     for(int gb=0;gb<groups;gb+=64){
       int ge=gb+64; if(ge>groups) ge=groups;

       for(int i=ib;i<ib+64;i+=8){
         for(int gg=gb;gg<ge;gg+=4){
           __m128i v0,v1,v2,v3,v4,v5,v6,v7;
           v0=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+0)*n+gg*8)),7)));
           v1=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+1)*n+gg*8)),7)));
           v2=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+2)*n+gg*8)),7)));
           v3=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+3)*n+gg*8)),7)));
           v4=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+4)*n+gg*8)),7)));
           v5=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+5)*n+gg*8)),7)));
           v6=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+6)*n+gg*8)),7)));
           v7=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+7)*n+gg*8)),7)));
           __m128i z0=_mm_unpacklo_epi16(_mm_unpacklo_epi8(v0,v1),_mm_unpacklo_epi8(v2,v3));
           __m128i z1=_mm_unpacklo_epi16(_mm_unpacklo_epi8(v4,v5),_mm_unpacklo_epi8(v6,v7));
           __m128i lo=_mm_unpacklo_epi32(z0,z1),hi=_mm_unpackhi_epi32(z0,z1);
           unsigned char*ti=tile+(gg-gb)*64+i-ib;
           _mm_storel_epi64((__m128i*)ti,lo);
           _mm_storeh_pd((double*)(ti+64),_mm_castsi128_pd(lo));
           _mm_storel_epi64((__m128i*)(ti+128),hi);
           _mm_storeh_pd((double*)(ti+192),_mm_castsi128_pd(hi));
         }
       }
       for(int gg=gb;gg<ge;gg++) memcpy(ix+(size_t)gg*IXS+ib, tile+(gg-gb)*64, 64);
     }
   }
 }
 alignas(64) __m256i tab[3][256];
 const __m256i shuf=_mm256_setr_epi8(0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3);
 const __m256i bits=_mm256_set1_epi64x(0x8040201008040201ULL),one=_mm256_set1_epi8(1),zero=_mm256_setzero_si256();
 for(int jb=0;jb<w;jb+=4){
  for(int g=0;g<groups;g+=3){
   const int cnt=groups-g<3?groups-g:3;
   for(int z=0;z<cnt;z++){
    __m256i*Tz=tab[z];
    build_gray8(pb+(size_t)((g+z)*8)*w+jb,w,Tz);
   }
   const unsigned char*x=ix+(size_t)g*IXS;
   if(g==0){
   if(cnt==3)apply_m<3,true>(n,pc,tab,x,IXS);
   else if(cnt==2)apply_m<2,true>(n,pc,tab,x,IXS);
   else if(cnt==1)apply_m<1,true>(n,pc,tab,x,IXS);
   } else {
   if(cnt==3)apply_m<3,false>(n,pc,tab,x,IXS);
   else if(cnt==2)apply_m<2,false>(n,pc,tab,x,IXS);
   else if(cnt==1)apply_m<1,false>(n,pc,tab,x,IXS);
   }
  }
  for(int i=0;i<n;i++){
   const uint32_t*row=(const uint32_t*)(pc+i);
   for(int t=0;t<8;t++){
    __m256i x=_mm256_shuffle_epi8(_mm256_set1_epi32(row[t]),shuf);
    x=_mm256_min_epu8(_mm256_and_si256(x,bits),one);
    _mm256_store_si256((__m256i*)(C+(size_t)i*n+jb*64+t*32),x);
   }
  }
 }
 free(pc);free(ix);free(pb);
}

CompilationN/AN/ACompile OKScore: N/A

Testcase #1644.83 us1 MB + 356 KBAcceptedScore: 100


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