// ===== REFERENCES =====
// [1] duck.ac 用户 saffah_codex_6a_agg3,提交 #121311 <https://duck.ac/submission/121311>
// —— 本文件正文的直接来源(本席入场时榜首 T = 112.842763 ms)。取回其公开源码后与本账号
// 在册最好件 #121232 逐 token diff:正文 = 本账号 #121232 的逐字节副本 + 1 处**真改动**
// (`probe_acc`/`qvalue`:谓词探测未命中时**保留已扫部分的最大值**,只补扫未被探测的中段,
// 不再重扫左右边界与前 32 个块;单叶块内的区间直接返回精确最大值)。落后量 0 · 有害 0。
// 该处**原样采用**(本席未改一个字节),全文引用链见下。
// [2] duck.ac 用户 saffah_cc_v41_agg1(本账号),提交 #121232 <https://duck.ac/submission/121232>
// —— 本文件正文的血缘(ARM FULLRANGE + ARM LNKLA + s_nxt 20 位打包 + t_nxt 3 字节打包 +
// 计数排序代替桶链表 + 分块扫描激活 + EBS 8)。其自带的 REFERENCES/思路 两块原样保留在下方。
// [3] 本账号更早血缘:#121138 <https://duck.ac/submission/121138>(对手 #120833 的三处真改动:
// letters_end 32B SIMD 分隔符扫描 · t_fresh 高水位免清零 · short_masks 小 n 掩码载入)、
// #120430(ARM FULLRANGE:整区间询问裁掉见证与裁剪机制)、#107930(ARM LNKLA:回退链链接前瞻)、
// #120833 / #120368(saffah_codex_6a_agg3:s_nxt 20 位打包路线)。
// [4] `problems/noi18c/notes.md`(第三/四轮相位账、`e18d_`/`o18d_`/`o18e_`/`o18f_` 席结论)。
// 许可证:以上均为 duck.ac 公开提交与公开题面,未见许可证声明;本文件按 RULES §3 逐项署名引用。
// ======================
// ===== 思路 =====
// (o18t_ 席,2026-10-02)
// 【口径(`python3 tools/exact.py noi18c` 现读,判题机原值)】
// mine = 114.500210(#121232)· T = 112.842763(#121311)⇒ 宽支 113.407977 已被对手件锁掉
// ⇒ 真栏 = 0.99*T+1µs = **111.715335**(需 −2.432%)。
//
// 【本发 = #121311 正文(逐位,含 qvalue)+ 本席两刀】
//
// ★★ 第一刀 ARM METASKIP:`build_ord()`/`build_meta()` 对"全 l=1,r=|S|"的家族是**纯死重**。
// 这两趟只产出 `ord_`/`mxo`,而 `mxo` 全文只有一处读者:clip 里的 `if(mxo[v] <= q.r) m = mxo[v];`。
// 整区间询问(l=1 && r>=|S|)按 ARM FULLRANGE 的证明**永不进 clip**,于是 tc16/tc17/tc6/tc7 付了
// 这两趟的钱却一个字节都不读。改成"第一次真正需要 clip 的询问前才建"(与既有的 eul_ready 惰性同一思路)。
// 判题机实测(本席同进程多臂探针,`o18t_probe1/3`,TSC 计):**META = 14.88M tick = 4.25 ms**,
// 正是这一刀在 tc17 上的兑现额。
//
// ★★★ 第二刀 ARM PAINT(本轮最大的一刀):**把对手的祖先涂色真正接到 clip 上**。
// 历史事实(本席逐文件核对 nc_paint.cpp / nc_paint2/3/4.cpp / e18d_lnkla.cpp / #121232):
// **`wl_last[]` 从落地那天起就只有写、没有读** —— clip 一直用 `mxo[v]`/`qmax(tin[v],tout[v])`,
// 从未读过涂色值。也就是说"涂色路线"当年被判负,真因是**它没接上**(在 tc17 上更是纯开销),
// 而不是涂色本身慢。
// 本发把它接上:激活位置 p 时从 `a=s_own[p]` 沿 `s_link` 一路向上涂 `wl_last[x]=p+1`,则
// `wl_last[x] = max{ own+1 : own < r, own ∈ subtree(x) }`
// —— 这正是 clip 要的 m(Euler 区间 [tin[v],tout[v]) 上的"已激活叶最大值",因为 v 的 link 子树
// 的"own"位置恰是 v 类在 r 之前的出现位置)。于是**整个 ev/ebmax/eb2 金字塔 + qvalue 塌缩成一次
// load**,并且连同 build_ord/build_meta/build_intervals/叶激活**全部不再需要**。
// 涂色是增量的(r 单调 ⇒ 值单调上升,永不回退、无需 reset);总代价 = Σ_i depth(s_own[i]) 步。
// 安全网两道:(a) 首次进 clip 时用 256 点采样估总步数,超过预算就整条走 Euler(对深链病态数据
// 近乎零成本地拒绝);(b) 涂色过程本身有运行预算 `10*(s_sz+1)` 步,超了就地回落 Euler
// (重建 meta+intervals + 回放全部已激活位置,语义不变)。
//
// 【判题机定价(本席 5 次同进程多臂 rdtsc 探针,零提交)】
// · ARM SPLITLOOP(把 full_range 分支提出循环):全区间族 −1.14% · 通用族 **+1.74%** ⇒ 通用族弃用,最终未采用。
// · ARM BLNKLA(build 的两条走链前瞻)判负(BUILD +0.69%)⇒ 未采用。
// · ★ ARM PAINT vs ARM EULER(`o18t_probe5`,|S|=5e5 r26 · Q=2e4 · Σ|T|=2e5 · l,r 随机,
// 16 轮轮转 8+8):**Euler 139.5M tick/轮 → Paint 86.9M tick/轮 = −37.7%**,两臂 checksum 全同
// (41fefe3e)。通用族(tc18..tc25)的 query 回路按此比例应掉 ~27 ms。
//
// 【正确性闸门】93 组语料(`/home/yjp/duck.ac/.cache/n1b/cv/`,覆盖 eul_ready=0/1、2/4/7/26 字母、
// 周期串、:full/:rand、|S|=500000 三组大测)**93/93 输出逐字节相同**;三条路线各单独强制跑过:
// 自动选路 / 强制 Euler / 强制 Paint 均 93/93;**强制小预算**(3*(s_sz+1),逼出"涂色超预算回落
// Euler"这条分支)亦 93/93。
// ======================
#include <string.h>
#include <stdint.h>
#include <immintrin.h>
#pragma GCC push_options
#pragma GCC target("avx2,popcnt,bmi,bmi2")
typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef unsigned long ul;
struct DI {
ul abi; const char*ip; ul is; char*op; ul ol,os; char*ep; ul el,es;
const char*IBp; ul IBl; char*OBp; ul OBl; ul tsc;
} __attribute__((packed));
#define MAXS 500005
#define MAXST 1000016
#define MAXQ 100005
#define MAXT 1000008
#define MAXTT 2000016
static u32 bhead[MAXS+8], szsub[MAXST], nxt[MAXST]; // bhead = len histogram -> start offsets
static u32 tin[MAXST], tout[MAXST], mxo[MAXST];
static u32 ord_[MAXST]; static int nord_; // len-descending state order (lazy-Euler)
static int eul_ready;
static int meta_ready;
static int route_ready; // 0 = no route chosen yet (nothing maintained)
static long long wl_work;
int act_g;
int o18t_force_route = -1; // probe hook: -1 auto, 0 Euler, 1 painting
// ancestor painting (see 思路): wl_last[x] = max own+1 among activated prefix-states
// whose suffix-link path passes through x == MX(x, r) exactly.
static u32 wl_last[MAXST];
static int wl_mode; static long long wl_budget;
// per-query paint-back scratch: zeroing only the states we painted keeps the
// reset O(sz) instead of O(MAXST) between queries.
static u32 plist_[MAXST];
// ======================= SAM of S (dense 26-way transitions) ==============
// transitions of state p are at s_nxt[p*26 .. p*26+26); every state index is
// used once per process, so BSS stays zero and fresh states have no transitions.
// Transitions packed as 24-bit little-endian words: 26*3 = 78 B per state instead of
// 26*4 = 104 B. State ids are <= 2*MAXS+8 < 2^24, so 24 bits are lossless.
// BUDGET (tc17: s_sz=661320) -- MEASURED sizes, not guesses:
// touched footprint 65.6 MB -> 49.2 MB (-16.4 MB) = -4197 first-touch pages.
// At the measured 244 ns/page that alone is -1.02 ms, and every sget in the SAM
// construction and in the match loop additionally reads a 25%-smaller random
// working set (better TLB reach), which is the part that should pay off more.
// COST: sget gains one AND; sset becomes a 4-byte read-modify-write (the extra byte
// read back is the *next* entry's low byte and is written back unchanged, so
// neighbours are preserved) = ~+3..5 uops per access. Reads use a 4-byte load at
// the 3-byte offset, so the array carries 10 spare rows of padding.
/* --- s_nxt: 20-bit packed transitions (row = 26*20 bits = 65 bytes) ---------
State ids are <= 2*MAXS+8 = 1000018 < 2^20, so 20 bits are lossless.
Row stride 65 B instead of 78 B (-17% footprint on the dominant table).
Entry c of row p starts at bit 20c; (20c)&7 == 4*(c&1) is always 0 or 4, so a
single unaligned 8-byte load at byte ((5c)>>1) always contains the entry. */
static u8 s_nxt[(2*MAXS+10)*65 + 16];
static u32 s_len[MAXST], s_link[MAXST];
static int s_sz, s_last;
static u32 s_own[MAXS];
#define S20_MASK 0xFFFFFu
static inline int sget(int p, int c){
size_t off = (size_t)p*65 + (size_t)(((unsigned)c*5u)>>1);
u32 x; __builtin_memcpy(&x, s_nxt + off, 4);
return (int)((x >> (((unsigned)c&1u)<<2)) & S20_MASK);
}
static inline void sset(int p, int c, int t){
size_t off = (size_t)p*65 + (size_t)(((unsigned)c*5u)>>1);
u32 x; __builtin_memcpy(&x, s_nxt + off, 4);
unsigned sh = ((unsigned)c&1u)<<2;
x = (x & ~(S20_MASK << sh)) | (((u32)t & S20_MASK) << sh);
__builtin_memcpy(s_nxt + off, &x, 4);
}
static void build_sam_s(const char* S, int n){
s_sz = 1; s_last = 1;
s_len[1] = 0; s_link[1] = 0;
memset(bhead, 0, (size_t)(n+2)*4);
for(int i=0;i<n;i++){
int c = S[i]-'a';
int cur = ++s_sz;
s_len[cur] = s_len[s_last]+1;
bhead[s_len[cur]]++;
mxo[cur] = (u32)(i+1);
s_own[i] = (u32)cur;
int p = s_last;
for(; p && !sget(p,c); p = (int)s_link[p]) sset(p,c,cur);
if(!p) s_link[cur] = 1;
else {
int q = sget(p,c);
if((int)s_len[p]+1 == (int)s_len[q]) s_link[cur] = (u32)q;
else {
int cl = ++s_sz;
s_len[cl] = s_len[p]+1; s_link[cl] = s_link[q];
bhead[s_len[cl]]++;
mxo[cl] = 0;
memcpy(s_nxt + (size_t)cl*65, s_nxt + (size_t)q*65, 65);
for(; p && sget(p,c)==q; p = (int)s_link[p]) sset(p,c,cl);
s_link[q] = (u32)cl; s_link[cur] = (u32)cl;
}
}
s_last = cur;
}
}
// ======================= SAM of T, rebuilt per query (dense) ==============
static u8 t_nxt[(2*MAXT+10)*80] __attribute__((aligned(16)));
static u32 t_len[MAXTT], t_link[MAXTT];
static int t_sz, t_last, t_fresh;
static inline int tget(int p, int c){
u32 x; __builtin_memcpy(&x, t_nxt + (size_t)p*80 + (size_t)c*3, 4);
return (int)(x & 0xFFFFFFu);
}
static inline void tset(int p, int c, int t){
size_t off = (size_t)p*80 + (size_t)c*3;
u32 x; __builtin_memcpy(&x, t_nxt + off, 4);
x = (x & 0xFF000000u) | ((u32)t & 0xFFFFFFu);
__builtin_memcpy(t_nxt + off, &x, 4);
}
static inline void tzero(int p){
if(p>t_fresh){t_fresh=p;return;}
u8* row = t_nxt + (size_t)p*80;
__m128i z = _mm_setzero_si128();
_mm_store_si128((__m128i*)(row+0), z);
_mm_store_si128((__m128i*)(row+16), z);
_mm_store_si128((__m128i*)(row+32), z);
_mm_store_si128((__m128i*)(row+48), z);
_mm_store_si128((__m128i*)(row+64), z);
}
// ============ suffix-link tree: len-bucket lists + two linear passes ======
static int s_sn;
// ---- counting sort of the states by len (replaces the bucket linked list) ----
// ord_ ends up len-ASCENDING, so pass 1 (bottom-up mxo) walks it backwards and
// pass 2 (top-down preorder) walks it forwards. No linked list => no serial
// pointer chase in the sweep, and no random write per state during construction.
static void build_ord(void){
int n = s_sn;
u32 sum = 0;
for(int L=0; L<=n; L++){ u32 c = bhead[L]; bhead[L] = sum; sum += c; }
for(u32 v=1; v<=(u32)s_sz; v++){ ord_[bhead[s_len[v]]++] = v; }
nord_ = (int)sum;
}
// Pass 1 (ALWAYS needed): mxo[v] = max own+1 over the subtree of v, by a
// len-descending sweep (children strictly before parents). The same sweep
// records the visiting order into ord_[] (sequential stores), so pass 2 never
// has to re-walk the bucket chains.
static void build_meta(void){ // bottom-up: children (larger len) first
for(int i = nord_-1; i >= 0; i--){
u32 v = ord_[i];
u32 p = s_link[v];
if(p && mxo[v] > mxo[p]) mxo[p] = mxo[v];
}
}
// Pass 2 (LAZY): subtree sizes + preorder numbers. Only the range-max over
// [tin,tout) uses these, so they are built at the first query that actually
// needs a range-max. ord_[] is len-descending, so forward = bottom-up sizes,
// backward = top-down preorder.
static void build_intervals(void){
// szsub[] is initialised here instead of in build_sam_s: it is only ever read by
// this function, so the per-state store in the construction loop (and the 2.4 MB of
// pages it dirtied) is pure overhead on tests that never build the intervals.
for(int i=0;i<nord_;i++) szsub[ord_[i]] = 1;
szsub[1] = 0; // state 1's size is never read (its link is 0), exactly as before
for(int i=nord_-1;i>=0;i--){ // len-descending = bottom-up sizes
u32 v = ord_[i]; u32 p = s_link[v];
if(p) szsub[p] += szsub[v];
}
tin[1] = 1; nxt[1] = 2;
for(int i=0;i<nord_;i++){ // len-ascending = parents first = preorder
u32 v = ord_[i];
if(v == 1) continue;
u32 p = s_link[v];
u32 t = nxt[p];
tin[v] = t; tout[v] = t + szsub[v];
nxt[p] = t + szsub[v];
nxt[v] = t + 1;
}
}
// ============ range max over the Euler array (fused block scans) ============
// Replaces the earlier "block prefix/suffix arrays + segment tree over blocks".
// MEASURED on the real judge (tools/probe.py, n=5e5 random 26-letter S, cycles):
// old eactivate 70.15M old build_meta 15.58M old build_intervals 22.38M
// old qmax ~96 cyc/call
// The old activation wrote a whole 64-entry prefix block AND a whole 64-entry
// suffix block per activation (512 B = 8 cache lines of RFO traffic), plus a
// 15-level segment-tree update -- 140 cycles per activation. With plain block
// scans the activation is 3 stores (one cold, two L1-resident) and the segment
// tree disappears.
#define EBS 8
#define EBSH 3
#define GBS 128
#define GBSH 7
#define ENB ((MAXST>>EBSH)+8)
#define ESB ((ENB>>GBSH)+8)
static u32 ev[MAXST+8];
static u32 ebmax[ENB+8];
static u32 eb2[ESB+8];
static inline void eactivate(int leaf, u32 val){
ev[leaf] = val;
int b = leaf >> EBSH;
ebmax[b] = val;
eb2[b >> GBSH] = val;
}
#define AVX2T __attribute__((target("avx2")))
AVX2T static inline __m256i ldu256(const void *p){
__m256i r; __asm__("vmovdqu %1, %0" : "=x"(r) : "m"(*(const __m256i *)p)); return r; }
// accumulate max over p[0..n) into acc for ANY n>=0 (n<8 must not use the
// overlapping tail load -- it would read BEFORE p and pick up foreign values)
static const u32 short_masks[8][8] __attribute__((aligned(32)))={{0,0,0,0,0,0,0,0},{0xffffffffu,0,0,0,0,0,0,0},{0xffffffffu,0xffffffffu,0,0,0,0,0,0},{0xffffffffu,0xffffffffu,0xffffffffu,0,0,0,0,0},{0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0,0,0,0},{0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0,0,0},{0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0,0},{0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0}};
AVX2T static inline __m256i accblk(__m256i acc, const u32* p, int n){
if(n <= 0) return acc;
if(n < 8){
if(n>=3)return _mm256_max_epu32(acc,_mm256_and_si256(ldu256(p),_mm256_load_si256((const __m256i*)short_masks[n])));
for(int i=0;i<n;i++) acc = _mm256_max_epu32(acc, _mm256_set1_epi32((int)p[i]));
return acc;
}
int i = 0;
for(; i+8 <= n; i += 8) acc = _mm256_max_epu32(acc, ldu256(p+i));
if(i < n) acc = _mm256_max_epu32(acc, ldu256(p+n-8)); // overlapping tail load
return acc;
}
AVX2T static inline u32 red256(__m256i acc){
__m128i lo = _mm256_castsi256_si128(acc), hi = _mm256_extracti128_si256(acc,1);
__m128i m = _mm_max_epu32(lo,hi);
m = _mm_max_epu32(m, _mm_shuffle_epi32(m,_MM_SHUFFLE(1,0,3,2)));
m = _mm_max_epu32(m, _mm_shuffle_epi32(m,_MM_SHUFFLE(2,3,0,1)));
return (u32)_mm_cvtsi128_si32(m);
}
AVX2T static inline u32 scanmax(const u32* p, int n){
if(n <= 0) return 0;
if(n==1)return p[0];
if(n==2)return p[0]>p[1]?p[0]:p[1];
return red256(accblk(_mm256_setzero_si256(), p, n));
}
static inline u32 qmax(int l, int r){ // max over [l,r)
if(l>=r) return 0;
int b1=l>>EBSH, b2=(r-1)>>EBSH;
if(b1==b2) return scanmax(ev+l, r-l);
__m256i acc = _mm256_setzero_si256();
acc = accblk(acc, ev+l, ((b1+1)<<EBSH) - l);
int j = r & (EBS-1);
if(j) acc = accblk(acc, ev+(b2<<EBSH), j);
else acc = accblk(acc, ebmax+b2, 1);
int lo=b1+1, hi=b2, cnt=hi-lo; // whole blocks [lo,hi)
if(cnt > 0){
if(cnt <= GBS){
acc = accblk(acc, ebmax+lo, cnt);
} else {
int s1=(lo+GBS-1)&~(GBS-1), s2=hi&~(GBS-1);
if(s1>lo) acc = accblk(acc, ebmax+lo, s1-lo);
if(s2>s1) acc = accblk(acc, eb2+(s1>>GBSH), (s2-s1)>>GBSH);
if(hi>s2) acc = accblk(acc, ebmax+s2, hi-s2);
}
}
return red256(acc);
}
// ================= o18f_ ARM FP: early-exit predicate on the Euler range =====
// qf(l,r,X,&w) answers "does some ACTIVATED leaf in [l,r) hold a value >= X",
// returning one such value in *w. It scans the cheapest sources first and stops
// at the first hit, so the common clip decision (lim >= len) is settled after
// 1-2 vector loads instead of a full two-level pyramid sweep.
static inline int anyge(const u32* p, int n, u32 X, u32* w){
if(n <= 0) return 0;
__m256i xm1 = _mm256_set1_epi32((int)X-1);
if(n >= 8){
int i=0;
for(; i+8 <= n; i += 8){
__m256i v = ldu256(p+i);
unsigned mk = (unsigned)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(v, xm1)));
if(mk){ *w = p[i+__builtin_ctz(mk)]; return 1; }
}
if(i < n){
int t = n-i;
__m256i v = _mm256_and_si256(ldu256(p+i), _mm256_load_si256((const __m256i*)short_masks[t]));
unsigned mk = (unsigned)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(v, xm1)));
if(mk){ *w = p[i+__builtin_ctz(mk)]; return 1; }
}
return 0;
}
__m256i v = _mm256_and_si256(ldu256(p), _mm256_load_si256((const __m256i*)short_masks[n]));
unsigned mk = (unsigned)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(v, xm1)));
if(mk){ *w = p[__builtin_ctz(mk)]; return 1; }
return 0;
}
#define QF_BUD 32
static inline int qf(int l, int r, u32 X, u32* w){
if(l >= r) return 0;
int b1=l>>EBSH, b2=(r-1)>>EBSH;
if(b1==b2) return anyge(ev+l, r-l, X, w);
if(anyge(ev+l, ((b1+1)<<EBSH) - l, X, w)) return 1;
int lo=b1+1, hi=b2, nb=hi-lo; int lim = nb<QF_BUD?nb:QF_BUD;
if(lim>0 && anyge(ebmax+lo, lim, X, w)) return 1;
int j=r&(EBS-1);
if(j){ if(anyge(ev+(b2<<EBSH), j, X, w)) return 1; }
else if(ebmax[b2]>=X){ *w = ebmax[b2]; return 1; }
return 0;
}
static inline int probe_acc(const u32* p, int n, u32 X, u32* w,__m256i &acc){
if(n <= 0) return 0;
__m256i xm1 = _mm256_set1_epi32((int)X-1);
if(n >= 8){
int i=0;
for(; i+8 <= n; i += 8){
__m256i v = ldu256(p+i);
unsigned mk = (unsigned)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(v, xm1)));
if(mk){ *w = p[i+__builtin_ctz(mk)]; return 1; }
acc=_mm256_max_epu32(acc,v);
}
if(i < n){
int t = n-i;
__m256i v = _mm256_and_si256(ldu256(p+i), _mm256_load_si256((const __m256i*)short_masks[t]));
unsigned mk = (unsigned)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(v, xm1)));
if(mk){ *w = p[i+__builtin_ctz(mk)]; return 1; }
acc=_mm256_max_epu32(acc,v);
}
return 0;
}
__m256i v = _mm256_and_si256(ldu256(p), _mm256_load_si256((const __m256i*)short_masks[n]));
unsigned mk = (unsigned)_mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(v, xm1)));
if(mk){ *w = p[__builtin_ctz(mk)]; return 1; }
acc=_mm256_max_epu32(acc,v);
return 0;
}
static inline u32 qvalue(int l,int r,u32 X){
if(l>=r)return 0;
int b1=l>>EBSH,b2=(r-1)>>EBSH;
if(b1==b2)return scanmax(ev+l,r-l);
__m256i acc=_mm256_setzero_si256();u32 w;
if(probe_acc(ev+l,((b1+1)<<EBSH)-l,X,&w,acc))return w;
int lo=b1+1,hi=b2,lim=hi-lo;if(lim>32)lim=32;
if(lim>0 && probe_acc(ebmax+lo,lim,X,&w,acc))return w;
lo+=lim;
int j=r&(EBS-1);
if(j){if(probe_acc(ev+(b2<<EBSH),j,X,&w,acc))return w;}
else{w=ebmax[b2];if(w>=X)return w;acc=_mm256_max_epu32(acc,_mm256_set1_epi32(w));}
int cnt=hi-lo;
if(cnt>0){if(cnt<=GBS)acc=accblk(acc,ebmax+lo,cnt);
else{int s1=(lo+GBS-1)&~(GBS-1),s2=hi&~(GBS-1);
if(s1>lo)acc=accblk(acc,ebmax+lo,s1-lo);
if(s2>s1)acc=accblk(acc,eb2+(s1>>GBSH),(s2-s1)>>GBSH);
if(hi>s2)acc=accblk(acc,ebmax+s2,hi-s2);}}
return red256(acc);
}
// ============================== query handling ============================
struct QE { const char* t; int tl, l, r, idx; };
static QE qs[MAXQ];
static QE qs2[MAXQ];
static long long answers[MAXQ];
static int f_[MAXS];
static int cnt_[MAXS+2];
static char* OUTP;
static const char D2[201] =
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
static inline void emit(long long v){
char b[24]; int c=0;
if(v==0){ *OUTP++='0'; *OUTP++='\n'; return; }
while(v >= 100){
unsigned r = (unsigned)(v % 100); v /= 100;
b[c++] = D2[r*2+1]; b[c++] = D2[r*2];
}
if(v >= 10){ unsigned r = (unsigned)v; b[c++] = D2[r*2+1]; b[c++] = D2[r*2]; }
else if(v > 0){ b[c++] = (char)('0' + (int)v); }
while(c) *OUTP++ = b[--c];
*OUTP++='\n';
}
static inline const char* letters_end(const char*p,const char*e){
while(e-p>=32){__m256i v=ldu256(p);u32 m=(u32)_mm256_movemask_epi8(_mm256_cmpgt_epi8(_mm256_set1_epi8('a'),v));if(m)return p+__builtin_ctz(m);p+=32;}
while(p<e&&*p>='a'&&*p<='z')p++;return p;
}
// ===== ARM PAINT (this seat): route helpers =============================
// Enable the painting route / fall back to the Euler route. o18t_go_euler() builds the two
// passes only the clip needs (ord_/mxo via build_ord/build_meta, then the block-maximum
// pyramid) and replays every already-activated position into the Euler structure, so the
// switch is invisible to the answer.
static void o18t_go_euler(void){
if(!meta_ready){ build_ord(); build_meta(); meta_ready = 1; }
build_intervals();
for(int i2=0;i2<act_g;i2++){ int vv=(int)s_own[i2]; eactivate((int)tin[vv], (u32)(i2+1)); }
eul_ready = 1;
}
// First clip of the test: pick the route. 256 states' suffix-link depths estimate the
// painting's total cost (sum_i depth(s_own[i])); the sampler aborts the moment the running
// estimate passes the budget, so a pathological string (a deep suffix-link tree) costs a few
// thousand steps to reject instead of a quadratic blow-up.
static void o18t_route_init(void){
route_ready = 1;
long long est = 0; int cnt = 0;
int step = s_sn/256; if(step < 1) step = 1;
for(int i = s_sn-1; i >= 0 && cnt < 256; i -= step){
int a = (int)s_own[i]; long long d = 0;
while(a != 1 && est + d < wl_budget){ a = (int)s_link[a]; d++; }
est += d; cnt++;
if(est >= wl_budget) break;
}
if(cnt) est = est * (long long)s_sn / cnt;
// accept the painting only when the estimate is HALF the running budget: the sampler can
// under-estimate a structured string, and the gap is what keeps a bad estimate from ever
// reaching the running budget (whose blow-up is the only way this route can cost extra).
int paint = (2*est <= wl_budget);
if(o18t_force_route >= 0) paint = o18t_force_route;
if(paint){
wl_mode = 1;
int i = 0;
for(; i<act_g; i++){
int a = (int)s_own[i]; u32 val = (u32)(i+1);
wl_work++;
while(a != 1){ wl_last[a] = val; a = (int)s_link[a]; wl_work++; }
if(wl_work > wl_budget) break;
}
if(i >= act_g) return;
wl_mode = 0;
}
o18t_go_euler();
}
static void run(const u8* in, ul insz, char* out){
const char* p = (const char*)in;
const char* e = p + insz;
OUTP = out;
while(p<e && (*p<'a'||*p>'z')) p++;
const char* sstart = p;
p=letters_end(p,e);
int sn = (int)(p - sstart);
build_sam_s(sstart, sn);
while(p<e && (*p<'0'||*p>'9')) p++;
int Q=0;
while(p<e && *p>='0'&&*p<='9'){ Q = Q*10 + (*p-'0'); p++; }
if(Q>MAXQ) Q=MAXQ;
for(int i=0;i<Q;i++){
while(p<e && (*p<'a'||*p>'z')) p++;
const char* ts = p;
p=letters_end(p,e);
int tl = (int)(p-ts);
if(tl>MAXT) tl=MAXT;
while(p<e && (*p<'0'||*p>'9')) p++;
int l=0; while(p<e && *p>='0'&&*p<='9'){ l=l*10+(*p-'0'); p++; }
while(p<e && (*p<'0'||*p>'9')) p++;
int r=0; while(p<e && *p>='0'&&*p<='9'){ r=r*10+(*p-'0'); p++; }
qs[i].t = ts; qs[i].tl = tl; qs[i].l = l; qs[i].r = r; qs[i].idx = i;
}
s_sn = sn;
int sz = s_sz;
// ---- strategy choice ---------------------------------------------------
// The ancestor-painting routes are KEPT in the file but no longer selected:
// real-judge measurement showed their cost is sum_i depth(s_own[i]) steps, which
// the 256-sample estimate can under-predict (a deep tree blows it up ~4x), while
// the Euler route is now bounded and cheap: with the block-scan activation its
// fixed cost is 34 cyc/state (intervals) + 20 cyc/activation + 113 cyc/qmax.
// Forcing the Euler route makes the tree phase predictable instead of data-dependent.
wl_mode = 0;
wl_budget = 10 * (long long)(sz + 1); // ARM PAINT: cap on the painting's total steps
eul_ready = 0;
meta_ready = 0;
route_ready = 0;
// ev / ebmax / etree are zero initialized (BSS): no clearing needed
{ // counting sort of the queries by r
int mx = sn+1;
memset(cnt_, 0, (size_t)(mx+2)*4);
for(int i=0;i<Q;i++){ int rr = qs[i].r; if(rr<0) rr=0; if(rr>mx) rr=mx; cnt_[rr]++; }
int acc=0;
for(int i=0;i<=mx;i++){ int c=cnt_[i]; cnt_[i]=acc; acc+=c; }
for(int i=0;i<Q;i++){ int rr = qs[i].r; if(rr<0) rr=0; if(rr>mx) rr=mx; qs2[cnt_[rr]++] = qs[i]; }
}
int act = 0; // positions < act already activated
wl_work = 0;
for(int qi=0; qi<Q; qi++){
const QE& q = qs2[qi];
int need = q.r; // activate positions 0..r-1
if(need > sn) need = sn;
if(need < 0) need = 0;
if(!route_ready){
// ARM METASKIP/PAINT: nothing is maintained until a query actually needs the clip.
if(need > act) act = need;
act_g = act;
} else if(wl_mode){ // ARM PAINT: paint each new position up its suffix-link path
int i = act;
for(; i<need; i++){
int a = (int)s_own[i]; u32 val = (u32)(i+1);
wl_work++;
while(a != 1){ wl_last[a] = val; a = (int)s_link[a]; wl_work++; }
if(wl_work > wl_budget) break; // budget blown -> fall back below
}
if(i < need){ act = need; act_g = act; wl_mode = 0; o18t_go_euler(); }
else { act = i; act_g = act; }
} else { // Euler route: leaf activate
for(; act<need; act++){
int v = (int)s_own[act];
eactivate((int)tin[v], (u32)(act+1));
}
act_g = act;
}
const char* T = q.t;
int tl = q.tl;
int l0 = q.l - 1;
int r1 = q.r - 1;
int maxlen = q.r - q.l + 1;
if(maxlen < 0) maxlen = 0;
int v = 1, len = 0, e = -1; // e = witness end position of the current match
// ARM FULLRANGE (this seat). For a FULL-RANGE query (l == 1 && r >= |S|) the interval
// clip can never shorten the match: mxo[v] >= s_len[v] >= len holds for every state, so
// the clip's `lim >= len` test fires on its very first iteration and leaves len alone,
// and maxlen = q.r - q.l + 1 >= |S| >= len makes the `len > maxlen` branch unreachable.
// The clip therefore degenerates to "record the witness e", and the witness feeds only
// the fast path -- which is itself a pure shortcut: whenever it fires, sget(v,c) != 0
// (the match extending at the witness is an occurrence of match+c in S), so the generic
// walk below exits on its FIRST test with exactly the same (v,len). Both are dead
// weight on this shape, and dropping them removes one mxo[] load per character plus the
// sstart[]/e bookkeeping from the loop that is 61% of tc17's runtime.
// Measured on this exact shape (|S|=5e5, |T|=1e6, l=1, r=|S|): the fast path fires
// 38,424 / 1,000,000 characters = 3.8%, and the clip runs 1.000 iterations per
// slow-path character => ~1.9 random loads/char are being paid for a 3.8% shortcut.
const int full_range = (l0 == 0 && q.r >= sn);
for(int i=0;i<tl;i++){
int c = T[i]-'a';
int nx=sget(v,c);
int vn=(int)s_link[v];
if(__builtin_expect(full_range,1)){
int ov = v;
while(v!=1 && !nx) { v = vn; vn = (int)s_link[v]; nx=sget(v,c); }
if(v != ov) len = (int)s_len[v];
if(nx){ v = nx; len++; } else { v = 1; len = 0; }
f_[i] = len;
continue;
}
// fast path: the match extends in place at the witness (f[i] <= f[i-1]+1, so len+1 is optimal)
if(len > 0 && e < r1 && sstart[e+1]-'a' == c){
v = nx; len++; e++;
f_[i] = len;
continue;
}
// Only the FINAL rung of this walk matters: `len` is rewritten on every step
// but read only after the loop, and once at least one step ran len == s_len[v].
// Reading s_len[] inside the loop therefore costs one extra random load per rung
// into a *separate* 2.4 MB array (one more TLB entry + one more miss) for
// nothing. The sget below is the very address the walk just tested => L1 hit.
{
int ov = v;
// ARM LNKLA: software-pipeline the suffix-link chase. The old form loaded
// s_link[v] only after the sget test failed, so each rung's link load sat on the
// critical path behind the previous rung. Fetching the next link up front makes
// the two loads of a rung independent, which is exactly the shape the current
// board leader uses (ref2/rival_107908.cpp, `int vn = (int)s_link[v];` hoisted
// above the test). `len` handling is unchanged: read once, at the rung we stop on.
while(v!=1 && !nx) { v = vn; vn = (int)s_link[v]; nx=sget(v,c); }
if(v != ov) len = (int)s_len[v];
}
if(nx){ v = nx; len++; } else { v = 1; len = 0; }
e = -1;
// clip the match to S[l..r]: longest suffix whose endpos hits [l+len-1, r-1]
while(len>0){
if(len > maxlen){
len = maxlen;
while(v != 1 && len <= (int)s_len[s_link[v]]) v = (int)s_link[v];
if(len == 0) break;
}
u32 m;
if(!route_ready) o18t_route_init(); // route must be settled BEFORE m is read
if(wl_mode){
// ARM PAINT -- the whole point: wl_last[v] IS the clip's m. Painting every
// activated position i up its suffix-link path makes
// wl_last[x] = max{ own+1 : own < r, own in subtree(x) },
// and the Euler interval maximum over [tin[v],tout[v]) asks for exactly that
// (the activated leaves of subtree(v) are the occurrences of v's class ending
// before r). So one array load replaces the whole ev/ebmax/eb2 pyramid sweep.
m = wl_last[v];
}
// NOTE: mxo is only the subtree maximum once build_meta has propagated it, so the
// fast path must not run before that (ARM PAINT defers build_ord/build_meta until the
// route is chosen, and the painting route never needs them at all).
else if(meta_ready && mxo[v] <= (u32)q.r) m = mxo[v];
else m = qvalue((int)tin[v],(int)tout[v],(u32)(len+l0));
// lengths in this state's class that can still satisfy the test
int lim = (int)m - l0;
if(lim >= len){ e = (int)m - 1; break; }
int pl = (int)s_len[s_link[v]];
if(lim > pl){ len = lim; e = (int)m - 1; break; }
len = pl;
if(len == 0) break;
v = (int)s_link[v];
}
f_[i] = len;
}
// SAM(T) -> g[], accumulate
t_sz=1; t_last=1;
t_len[1]=0; t_link[1]=0;
tzero(1);
long long ans=0;
for(int i=0;i<tl;i++){
int c = T[i]-'a';
int cur = ++t_sz;
t_len[cur] = t_len[t_last]+1; t_link[cur]=0;
tzero(cur);
int pp = t_last;
for(; pp && !tget(pp,c); pp = (int)t_link[pp]) tset(pp,c,cur);
int lk;
if(!pp) lk = 1;
else {
int qq = tget(pp,c);
if((int)t_len[pp]+1 == (int)t_len[qq]) lk = qq;
else {
int cl = ++t_sz;
if(cl>t_fresh)t_fresh=cl;
t_len[cl] = t_len[pp]+1; t_link[cl] = t_link[qq];
memcpy(t_nxt + (size_t)cl*80, t_nxt + (size_t)qq*80, 80);
for(; pp && tget(pp,c)==qq; pp = (int)t_link[pp]) tset(pp,c,cl);
t_link[qq] = (u32)cl; lk = cl;
}
}
t_link[cur] = (u32)lk;
t_last = cur;
int g = (int)t_len[lk];
int m = g > f_[i] ? g : f_[i];
ans += (long long)(i+1) - m;
}
answers[q.idx] = ans;
}
for(int i=0;i<Q;i++) emit(answers[i]);
}
#pragma GCC pop_options
#ifndef LOCAL
int main(){ return 0; }
extern "C" void __libc_start_main(void*mm,int argc,char**argv){
ul *p=(ul*)(argv+argc+1);
while(*p) p++; p++;
DI *d=0; for(; p[0]; p+=2) if(p[0]==0x6b637564UL){ d=(DI*)p[1]; break; }
if(d){
const u8* in=(const u8*)d->IBp; ul insz=d->IBl;
if(!insz && d->is){ in=(const u8*)d->ip; insz=d->is; }
run(in,insz,d->op);
d->os=(ul)(OUTP-d->op);
}
__asm__ volatile("syscall"::"a"(60),"D"(0):"rcx","r11","memory");
for(;;);
}
#endif