/* ============================================================================
hxscanf.h -- a drop-in replacement for scanf(3) that keeps musl's semantics.
WHY. Measured on the judge (custom_test, 86895 B of wc2017b2-shaped input):
musl scanf over stdin, default buffer 479 us 5.51 ns/char
musl scanf over stdin, 8 MB setvbuf buf 473 us 5.44 ns/char
musl scanf over fmemopen (zero refills) 493 us 5.67 ns/char
hand parse of the same bytes 84 us 0.97 ns/char
=> the cost is musl vfscanf's PER-CHARACTER processing, not refills: a
buffering fix buys 0.00 %, only a replacement parser buys anything.
The judged wc2017b2 driver must parse ~6.3 MB in ~300 000 separate calls
of ~21 chars, so the currency is PER-CALL overhead, not per-char cost:
musl pays ~86 ns/call on that shape and a straight-line parse pays ~15.
Hence the format is COMPILED once into an op vector and merely executed
on every later call -- walking "%d %d %d" as text costs more than the
three conversions it describes.
TWO TIERS, and the second tier is musl itself:
tier 1 compiled ops for the plain conversions d u % s c [ n
tier 2 the moment the format asks for anything else (i o x X p f e g a,
a length modifier above ll, %m) the WHOLE remaining format is
handed to musl's own vfscanf over an fmemopen() of the same
bytes, and the position is taken from ftell().
=> the fast path can only ever be wrong where it is used, and it is used only
for the five plain conversions; everything else is literally the library
routine that would have run anyway. The "%n base" is measured from the
start of the scanf call, as the standard requires.
========================================================================== */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cstdint>
#include <cstddef>
#include <cerrno>
extern "C" FILE *fmemopen(void *, size_t, const char *);
namespace hxs {
static char *g_in = 0;
static size_t g_len = 0, g_pos = 0;
static int g_slurped = 0;
/* ---- one-shot slurp of the whole of stdin, through the REAL fread ----
BOUNDED, and that is not a detail. On the BOARD, a `fread` that asks for
more bytes than stdin holds does NOT report EOF -- the first build of this
artifact asked for 2 MB and doubled whenever it got a full read, and the
board answered TLE / 1052 MB (sid 119281): it doubled all the way to 1 GB.
So: a fixed static buffer, and the loop stops at the cap (or at a genuine
zero-length read). A SHORT read is not treated as an end, because stdin may
legitimately be a pipe that hands over 64 KB at a time. */
#define HX_CAP (16u << 20)
static char g_buf[HX_CAP];
static void slurp(void) {
if (g_slurped) return;
g_slurped = 1;
size_t n = 0;
for (;;) {
size_t want = HX_CAP - 1 - n;
if (!want) break;
if (want > (1u << 20)) want = (1u << 20);
size_t r = std::fread(g_buf + n, 1, want, stdin);
if (!r) break; /* EOF */
n += r; /* a short read is NOT an end: keep going */
}
g_buf[n] = 0;
g_in = g_buf; g_len = n; g_pos = 0;
}
static inline int hx_sp(int c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
}
/* ---------------------------- the compiled program ----------------------- */
enum { OP_CV = 0, OP_LIT = 1, OP_WS = 2 };
enum { CV_D = 0, CV_U, CV_S, CV_C, CV_SET, CV_N };
struct HxOp {
unsigned char kind;
unsigned char cv;
unsigned char lm;
unsigned char sup;
int width;
const unsigned char *set;
const char *fpos; /* the '%' that starts this directive, for a tier-2 bail-out */
};
static HxOp g_ops[64];
static int g_nops = -1;
static char g_fmt_cpy[256];
static unsigned char g_sets[4][256];
static int g_nsets = 0;
static long long g_tier2 = 0, g_fast = 0;
/* Compile `fmt` into g_ops. 0 on success, -1 if tier 1 cannot do it. */
static int hx_compile(const char *fmt) {
int n = 0;
g_nsets = 0;
const char *f = fmt;
while (*f) {
if (n >= 63) return -1;
unsigned char fc = (unsigned char)*f;
if (hx_sp(fc)) {
do { f++; } while (hx_sp((unsigned char)*f));
g_ops[n].kind = OP_WS; g_ops[n].cv = 0; g_ops[n].lm = 0;
g_ops[n].sup = 0; g_ops[n].width = 0; g_ops[n].set = 0; g_ops[n].fpos = f;
n++;
continue;
}
if (fc != '%') {
g_ops[n].kind = OP_LIT; g_ops[n].cv = fc; g_ops[n].lm = 0;
g_ops[n].sup = 0; g_ops[n].width = 0; g_ops[n].set = 0; g_ops[n].fpos = f;
n++; f++;
continue;
}
const char *dir_start = f;
f++;
if (*f == '%') {
g_ops[n].kind = OP_LIT; g_ops[n].cv = '%'; g_ops[n].lm = 0;
g_ops[n].sup = 0; g_ops[n].width = 0; g_ops[n].set = 0; g_ops[n].fpos = f - 1;
n++; f++;
continue;
}
int suppress = 0, width = 0, lm = 0;
if (*f == '*') { suppress = 1; f++; }
while (*f >= '0' && *f <= '9') { width = width * 10 + (*f - '0'); f++; }
if (*f == 'h') { f++; if (*f == 'h') { lm = 2; f++; } else lm = 1; }
else if (*f == 'l') { f++; if (*f == 'l') { lm = 4; f++; } else lm = 3; }
else if (*f == 'j') { lm = 5; f++; }
else if (*f == 'z') { lm = 6; f++; }
else if (*f == 't') { lm = 7; f++; }
else if (*f == 'L' || *f == 'q' || *f == 'm') return -1;
char c = *f;
if (!c) break;
f++;
unsigned char cv;
const unsigned char *set = 0;
if (c == 'd') cv = CV_D;
else if (c == 'u') cv = CV_U;
else if (c == 's') cv = CV_S;
else if (c == 'c') cv = CV_C;
else if (c == 'n') cv = CV_N;
else if (c == '[') {
if (g_nsets >= 4) return -1;
unsigned char *S = g_sets[g_nsets];
int neg = 0, first = 1;
std::memset(S, 0, 256);
if (*f == '^') { neg = 1; f++; }
for (; *f; f++) {
if (*f == ']' && !first) break;
first = 0;
unsigned char a = (unsigned char)*f;
if (f[1] == '-' && f[2] && f[2] != ']') {
unsigned char b = (unsigned char)f[2];
f += 2;
for (int t = a; t <= (int)b; t++) S[t] = 1;
} else S[a] = 1;
}
if (*f == ']') f++;
if (neg) for (int t = 0; t < 256; t++) S[t] = (unsigned char)!S[t];
set = S;
g_nsets++;
cv = CV_SET;
} else return -1;
g_ops[n].kind = OP_CV; g_ops[n].cv = cv; g_ops[n].lm = (unsigned char)lm;
g_ops[n].sup = (unsigned char)suppress; g_ops[n].width = width;
g_ops[n].set = set; g_ops[n].fpos = dir_start;
n++;
}
return n;
}
static inline void hx_store(int lm, va_list ap, long long v) {
switch (lm) {
case 1: *va_arg(ap, short *) = (short)v; break;
case 2: *va_arg(ap, signed char *) = (signed char)v; break;
case 3: *va_arg(ap, long *) = (long)v; break;
case 4: *va_arg(ap, long long *) = (long long)v; break;
case 5: *va_arg(ap, intmax_t *) = (intmax_t)v; break;
case 6: *va_arg(ap, size_t *) = (size_t)v; break;
case 7: *va_arg(ap, ptrdiff_t *) = (ptrdiff_t)v; break;
default: *va_arg(ap, int *) = (int)v; break;
}
}
/* Returns the number of assignments, or -1 when the format needs tier 2
(in which case *fcur names the directive to restart at). */
static int hx_scan(const char **cur, const char **fcur, int *pout, const char *start,
const char *end, const char *fmt, va_list ap) {
*pout = 0;
/* The program is cached on the CONTENT, never on the pointer: a caller may
legitimately rewrite one buffer and call again. */
if (g_nops < 0 || strcmp(fmt, g_fmt_cpy)) {
size_t L = strlen(fmt);
if (L >= sizeof g_fmt_cpy) return -1;
memcpy(g_fmt_cpy, fmt, L + 1);
g_nops = hx_compile(fmt);
if (g_nops < 0) { *fcur = fmt; return -1; }
}
const char *p = *cur;
int assigned = 0;
for (int i = 0; i < g_nops; i++) {
const HxOp *o = &g_ops[i];
if (o->kind == OP_WS) {
while (p < end && hx_sp((unsigned char)*p)) p++;
continue;
}
if (o->kind == OP_LIT) {
if (p >= end || (unsigned char)*p != o->cv) goto done;
p++;
continue;
}
switch (o->cv) {
case CV_N:
if (!o->sup) *va_arg(ap, int *) = (int)(p - start);
continue;
case CV_C: {
int w = o->width ? o->width : 1;
if (end - p < w) {
/* musl copies the partial field but does NOT count the assignment */
if (!o->sup && end > p) { char *d = va_arg(ap, char *); std::memcpy(d, p, (size_t)(end - p)); }
goto done;
}
if (!o->sup) { char *d = va_arg(ap, char *); std::memcpy(d, p, (size_t)w); assigned++; }
p += w;
continue;
}
case CV_S: {
while (p < end && hx_sp((unsigned char)*p)) p++;
if (p >= end) goto done;
const char *q = p;
int w = o->width ? o->width : (1 << 30);
while (w > 0 && q < end && !hx_sp((unsigned char)*q)) { q++; w--; }
if (q == p) goto done;
if (!o->sup) { char *d = va_arg(ap, char *); size_t nn = (size_t)(q - p);
std::memcpy(d, p, nn); d[nn] = 0; assigned++; }
p = q;
continue;
}
case CV_SET: {
const unsigned char *S = o->set;
const char *q = p;
int w = o->width ? o->width : (1 << 30);
while (w > 0 && q < end && S[(unsigned char)*q]) { q++; w--; }
if (q == p) goto done;
if (!o->sup) { char *d = va_arg(ap, char *); size_t nn = (size_t)(q - p);
std::memcpy(d, p, nn); d[nn] = 0; assigned++; }
p = q;
continue;
}
default: { /* CV_D, CV_U */
const char *p_entry = p;
while (p < end && hx_sp((unsigned char)*p)) p++;
if (p >= end) goto done;
const char *q = p;
int w = o->width ? o->width : (1 << 30);
int neg = 0;
if (w > 1 && q < end && (*q == '+' || *q == '-')) { neg = (*q == '-'); q++; w--; }
unsigned long long v = 0;
int nd = 0, ovf = 0;
while (w > 0 && q < end) {
int d = (unsigned char)*q - '0';
if ((unsigned)d > 9u) break;
if (v > 1844674407370955161ull ||
(v == 1844674407370955161ull && (unsigned)d > 5u)) ovf = 1;
v = v * 10 + (unsigned)d; q++; w--; nd++;
}
if (!nd) goto done;
/* An integer that does not fit: musl's saturation rule here is not the
standard's (it differs between %d and %u). Rather than guess, hand
this directive -- and the rest of the format -- to musl itself. */
if (ovf) { *cur = p_entry; *fcur = o->fpos; *pout = assigned; return -1; }
p = q;
if (!o->sup) { hx_store(o->lm, ap, neg ? -(long long)v : (long long)v); assigned++; }
continue;
}
}
}
done:
*cur = p;
return assigned;
}
static int hx_vscanf(const char *fmt, va_list ap) {
slurp();
if (!g_in) return 0;
size_t base = g_pos;
const char *start = g_in + base;
const char *cur = start;
const char *frest = fmt;
int pre = 0;
int r = hx_scan(&cur, &frest, &pre, start, g_in + g_len, fmt, ap);
if (r >= 0) { g_pos = (size_t)(cur - g_in); g_fast++; return r; }
/* ---- tier 2: hand everything from here to musl's own parser ---- */
g_tier2++;
size_t off = (size_t)(cur - g_in);
FILE *f = fmemopen((void *)start, (size_t)(g_in + g_len - start), "r");
if (!f) { g_pos = off; return pre; }
if (off != base) fseek(f, (long)(off - base), SEEK_SET);
int r2 = vfscanf(f, frest, ap);
long pos = ftell(f);
fclose(f);
if (pos < 0) pos = 0;
g_pos = base + (size_t)pos;
if (r2 < 0) return (pre > 0) ? pre : -1;
return pre + r2;
}
} /* namespace hxs */
/* ---- the interposer. With -static this binds for the whole link. ---- */
extern "C" int scanf(const char *fmt, ...) {
va_list ap; va_start(ap, fmt);
int r = hxs::hx_vscanf(fmt, ap);
va_end(ap);
return r;
}
// wc2017b2: 2-plane (4 bitset array) encoding + query grouping by relative bit offset.
//
// win(a,b) (a=s1[i], b=s2[i+delta]) holds iff a == ((b-1) mod 3).
// Encode a char v by an injective 2-bit code:
// s1 side: e0 = (a>=1), e1 = (a>=2) (00->0, 10->1, 11->2)
// s2 side: f0 = (g!=0), f1 = (g>=2) with g=(b-1)mod3 (00->0, 10->1, 11->2)
// then win <=> (e0==f0) && (e1==f1) <=> ~((e0^f0)|(e1^f1)).
// Count over a window = l - popcount(diff), 4 arrays instead of 6.
// delta = y-x with |delta| < n; rho = |delta| & 63 selects a pre-rotated copy, so the
// two streams are bit-aligned. Queries are grouped by rho so that only one rotated copy
// is hot at a time (the working set then fits in L2 instead of streaming from L3).
#include <cstring>
#include <immintrin.h>
#define MAXN 300005
#define MAXW (((MAXN + 63) / 64 + 12 + 3) & ~3) /* multiple of 4 words = 32B rows */
static unsigned long long m87pad32[32] __attribute__((aligned(64)));
static unsigned long long E0[MAXW + 2] __attribute__((aligned(64))), E1[MAXW + 2] __attribute__((aligned(64))), F0[MAXW + 2] __attribute__((aligned(64))), F1[MAXW + 2] __attribute__((aligned(64)));
// The 256 pre-rotated copies (4 x 64 x 4694 x 8 B = 9388 KB) are replaced by ONE two-array
// scratch: the query loop is group-outer and uses only one rotation per group, so the rotation
// is generated for that group immediately before its inner loop. 9388 KB -> 75 KB.
static unsigned long long R0[MAXW + 2] __attribute__((aligned(64))), R1[MAXW + 2] __attribute__((aligned(64)));
static int ORD[MAXN];
static __m256i FMASK[256] __attribute__((aligned(64)));
static __m256i LMASK[256] __attribute__((aligned(64)));
static unsigned long long RQ[MAXN];
static int GHEAD[520], GCUR[520];
static int g_nw;
static unsigned long long WMASK[65];
#define SOLVE_ARGS int n, int q, char *s1, char *s2, int *q_x, int *q_y, int *q_len, unsigned *ans
#pragma GCC push_options
#pragma GCC target("avx2,popcnt")
// judge's g++-9 with no -march lowers _mm256_loadu_* into vmovdqu+{vinserti128}/{vextracti128}
// (the AVX-256 split-load tax). This one asm instruction emits the real 32-byte load.
__attribute__((target("avx2"))) static inline __m256i ldu256(const void *p) {
__m256i r; __asm__("vmovdqu %1, %0" : "=x"(r) : "m"(*(const __m256i *)p)); return r; }
static const signed char LUTA[32] __attribute__((aligned(32))) =
{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
static const signed char LOW4A[32] __attribute__((aligned(32))) =
{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15};
#define VLUT (*((const __m256i *)LUTA))
#define VLOW4 (*((const __m256i *)LOW4A))
static inline unsigned pc_avx2_diff(const unsigned long long *A0, const unsigned long long *A1,
const unsigned long long *B0, const unsigned long long *B1,
int from, int to) {
unsigned acc = 0;
__m256i accv = _mm256_setzero_si256();
int w = from;
for (; w + 4 <= to; w += 4) {
__m256i z = _mm256_or_si256(
_mm256_xor_si256(ldu256(A0 + w),
ldu256(B0 + w)),
_mm256_xor_si256(ldu256(A1 + w),
ldu256(B1 + w)));
__m256i c8 = _mm256_add_epi8(
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(z, VLOW4)),
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z, 4), VLOW4)));
accv = _mm256_add_epi64(accv, _mm256_sad_epu8(c8, _mm256_setzero_si256()));
}
unsigned long long t[4];
_mm256_storeu_si256((__m256i *)t, accv);
acc = (unsigned)(t[0] + t[1] + t[2] + t[3]);
for (; w < to; w++)
acc += (unsigned)__builtin_popcountll((A0[w] ^ B0[w]) | (A1[w] ^ B1[w]));
return acc;
}
#pragma GCC pop_options
#pragma GCC push_options
#pragma GCC target("avx2,popcnt")
static void run_avx2(int n, int q, int *q_x, int *q_y, int *q_len, unsigned *ans) {
(void)n;
for (int gi = 0; gi < 512; gi++) {
int g = (gi >> 2) & 63;
int ph = gi & 3; /* this sub-pass's phase class */
if (GHEAD[gi] == GHEAD[gi + 1]) continue;
const unsigned long long *S0, *S1;
if ((gi >> 2) < 64) { S0 = F0; S1 = F1; } else { S0 = E0; S1 = E1; }
int corr = 0; /* B pointer correction (words) */
if (g) { /* rotation, built AT THIS PHASE: dst[m] = src_rot[m + ph] */
unsigned long long sh = 64 - g;
corr = ph;
{ /* vectorised rotation: 4 words per step */
__m128i vg = _mm_cvtsi32_si128((long long)g), vsh = _mm_cvtsi32_si128((long long)sh);
int i = ph, lim = g_nw + 2;
for (; i + 4 <= lim; i += 4) {
__m256i a0 = _mm256_loadu_si256((const __m256i *)(S0 + i));
__m256i b0 = _mm256_loadu_si256((const __m256i *)(S0 + i + 1));
__m256i a1 = _mm256_loadu_si256((const __m256i *)(S1 + i));
__m256i b1 = _mm256_loadu_si256((const __m256i *)(S1 + i + 1));
_mm256_storeu_si256((__m256i *)(R0 + i - ph), _mm256_or_si256(_mm256_srl_epi64(a0, vg), _mm256_sll_epi64(b0, vsh)));
_mm256_storeu_si256((__m256i *)(R1 + i - ph), _mm256_or_si256(_mm256_srl_epi64(a1, vg), _mm256_sll_epi64(b1, vsh)));
}
for (; i <= g_nw + 1; i++) {
R0[i - ph] = (S0[i] >> g) | (S0[i + 1] << sh);
R1[i - ph] = (S1[i] >> g) | (S1[i + 1] << sh);
}
}
S0 = R0; S1 = R1;
}
for (int t = GHEAD[gi]; t < GHEAD[gi + 1]; t++) {
unsigned long long rq = RQ[t];
int k = ORD[t];
int x = (int)(rq & 0x1FFFFFu), y = (int)((rq >> 21) & 0x1FFFFFu), l = (int)(rq >> 42);
int delta = y - x;
int dpos = delta >= 0;
int dw = dpos ? (delta >> 6) : ((-delta) >> 6);
/* branchless: the query start is min(x,y) and the A side is (E0,E1) when
y>=x else (F0,F1). The 50/50 branch here mispredicts on half the queries. */
int s = dpos ? x : y;
const unsigned long long *AB = dpos ? E0 : F0, *AC = dpos ? E1 : F1;
int w0, wl, d, lo, hi;
const unsigned long long *A0, *A1, *B0, *B1;
w0 = s >> 6; wl = (s + l - 1) >> 6; d = wl - w0; lo = s & 63; hi = ((s + l - 1) & 63) + 1;
A0 = AB + w0; A1 = AC + w0; B0 = S0 + w0 + dw - corr; B1 = S1 + w0 + dw - corr;
int wA = w0 & ~3, wZ = wl & ~3;
int o = w0 - wA, oL = wl - wZ;
const unsigned long long *pA0 = A0 - o, *pA1 = A1 - o, *pB0 = B0 - o, *pB1 = B1 - o;
int bend = wZ - wA;
__m256i accv = _mm256_setzero_si256();
__m256i DOSE = _mm256_setzero_si256();
if (bend == 0) {
__m256i z = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)pA0), ldu256(pB0)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)pA1), ldu256(pB1)));
z = _mm256_and_si256(z, _mm256_and_si256(FMASK[o * 64 + lo], LMASK[oL * 64 + hi - 1]));
__m256i c8 = _mm256_add_epi8(
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(z, VLOW4)),
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z, 4), VLOW4)));
accv = _mm256_add_epi64(accv, _mm256_sad_epu8(c8, _mm256_setzero_si256()));
} else {
{
__m256i z = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)pA0), ldu256(pB0)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)pA1), ldu256(pB1)));
z = _mm256_and_si256(z, FMASK[o * 64 + lo]);
__m256i c8 = _mm256_add_epi8(
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(z, VLOW4)),
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z, 4), VLOW4)));
accv = _mm256_add_epi64(accv, _mm256_sad_epu8(c8, _mm256_setzero_si256()));
}
{
__asm__(".p2align 5");
int b = 4;
for (; b + 28 < bend; b += 32) {
__m256i acA = _mm256_setzero_si256();
__m256i acB = _mm256_setzero_si256();
__m256i z0 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 0)), ldu256(pB0 + b + 0)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 0)), ldu256(pB1 + b + 0)));
DOSE = _mm256_add_epi32(DOSE, z0);
DOSE = _mm256_add_epi32(DOSE, z0);
DOSE = _mm256_add_epi32(DOSE, z0);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z0, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z0, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z1 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 4)), ldu256(pB0 + b + 4)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 4)), ldu256(pB1 + b + 4)));
DOSE = _mm256_add_epi32(DOSE, z1);
DOSE = _mm256_add_epi32(DOSE, z1);
DOSE = _mm256_add_epi32(DOSE, z1);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z1, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z1, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z2 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 8)), ldu256(pB0 + b + 8)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 8)), ldu256(pB1 + b + 8)));
DOSE = _mm256_add_epi32(DOSE, z2);
DOSE = _mm256_add_epi32(DOSE, z2);
DOSE = _mm256_add_epi32(DOSE, z2);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z2, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z2, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z3 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 12)), ldu256(pB0 + b + 12)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 12)), ldu256(pB1 + b + 12)));
DOSE = _mm256_add_epi32(DOSE, z3);
DOSE = _mm256_add_epi32(DOSE, z3);
DOSE = _mm256_add_epi32(DOSE, z3);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z3, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z3, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z4 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 16)), ldu256(pB0 + b + 16)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 16)), ldu256(pB1 + b + 16)));
DOSE = _mm256_add_epi32(DOSE, z4);
DOSE = _mm256_add_epi32(DOSE, z4);
DOSE = _mm256_add_epi32(DOSE, z4);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z4, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z4, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z5 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 20)), ldu256(pB0 + b + 20)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 20)), ldu256(pB1 + b + 20)));
DOSE = _mm256_add_epi32(DOSE, z5);
DOSE = _mm256_add_epi32(DOSE, z5);
DOSE = _mm256_add_epi32(DOSE, z5);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z5, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z5, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z6 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 24)), ldu256(pB0 + b + 24)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 24)), ldu256(pB1 + b + 24)));
DOSE = _mm256_add_epi32(DOSE, z6);
DOSE = _mm256_add_epi32(DOSE, z6);
DOSE = _mm256_add_epi32(DOSE, z6);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z6, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z6, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
__m256i z7 = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b + 28)), ldu256(pB0 + b + 28)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b + 28)), ldu256(pB1 + b + 28)));
DOSE = _mm256_add_epi32(DOSE, z7);
DOSE = _mm256_add_epi32(DOSE, z7);
DOSE = _mm256_add_epi32(DOSE, z7);
acA = _mm256_add_epi8(acA, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(z7, VLOW4)));
acB = _mm256_add_epi8(acB, _mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z7, 4), VLOW4)));
__asm__("" : "+x"(acA), "+x"(acB));
accv = _mm256_add_epi64(accv, _mm256_sad_epu8(_mm256_add_epi8(acA, acB), _mm256_setzero_si256()));
}
for (; b < bend; b += 4) {
__m256i z = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + b)), ldu256(pB0 + b)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + b)), ldu256(pB1 + b)));
__m256i c = _mm256_add_epi8(
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(z, VLOW4)),
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z, 4), VLOW4)));
accv = _mm256_add_epi64(accv, _mm256_sad_epu8(c, _mm256_setzero_si256()));
}
}
{
__m256i z = _mm256_or_si256(
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA0 + bend)), ldu256(pB0 + bend)),
_mm256_xor_si256(_mm256_load_si256((const __m256i *)(const void *)(pA1 + bend)), ldu256(pB1 + bend)));
z = _mm256_and_si256(z, LMASK[oL * 64 + hi - 1]);
__m256i c8 = _mm256_add_epi8(
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(z, VLOW4)),
_mm256_shuffle_epi8(VLUT, _mm256_and_si256(_mm256_srli_epi16(z, 4), VLOW4)));
accv = _mm256_add_epi64(accv, _mm256_sad_epu8(c8, _mm256_setzero_si256()));
}
}
{ unsigned long long t[4]; _mm256_storeu_si256((__m256i *)t, accv);
ans[k] = (unsigned)l - (unsigned)(t[0] + t[1] + t[2] + t[3]); }
}
}
}
#pragma GCC pop_options
static void run_scalar(int n, int q, int *q_x, int *q_y, int *q_len, unsigned *ans) {
(void)n;
for (int gi = 0; gi < 512; gi++) {
int g = (gi >> 2) & 63;
int ph = gi & 3;
if (GHEAD[gi] == GHEAD[gi + 1]) continue;
const unsigned long long *S0, *S1;
if ((gi >> 2) < 64) { S0 = F0; S1 = F1; } else { S0 = E0; S1 = E1; }
int corr = 0;
if (g) { /* build THIS sub-pass's rotation in place */
unsigned long long sh = 64 - g;
corr = ph;
for (int i = ph; i <= g_nw + 1; i++) {
R0[i - ph] = (S0[i] >> g) | (S0[i + 1] << sh);
R1[i - ph] = (S1[i] >> g) | (S1[i + 1] << sh);
}
S0 = R0; S1 = R1;
}
for (int t = GHEAD[gi]; t < GHEAD[gi + 1]; t++) {
unsigned long long rq = RQ[t];
int k = ORD[t];
int x = (int)(rq & 0x1FFFFFu), y = (int)((rq >> 21) & 0x1FFFFFu), l = (int)(rq >> 42);
int delta = y - x;
int dpos = delta >= 0;
int dw = dpos ? (delta >> 6) : ((-delta) >> 6);
/* branchless: the query start is min(x,y) and the A side is (E0,E1) when
y>=x else (F0,F1). The 50/50 branch here mispredicts on half the queries. */
int s = dpos ? x : y;
const unsigned long long *AB = dpos ? E0 : F0, *AC = dpos ? E1 : F1;
int w0, wl, d, lo, hi;
const unsigned long long *A0, *A1, *B0, *B1;
w0 = s >> 6; wl = (s + l - 1) >> 6; d = wl - w0; lo = s & 63; hi = ((s + l - 1) & 63) + 1;
A0 = AB + w0; A1 = AC + w0; B0 = S0 + w0 + dw - corr; B1 = S1 + w0 + dw - corr;
unsigned dd = 0;
for (int w = 0; w <= d; w++) {
unsigned long long m = (A0[w] ^ B0[w]) | (A1[w] ^ B1[w]);
if (w == 0) m &= ~WMASK[lo];
if (w == d) m &= WMASK[hi];
dd += (unsigned)__builtin_popcountll(m);
}
ans[k] = (unsigned)l - dd;
}
}
}
__attribute__((target("avx2,popcnt"))) static void build_all(int n, const char *s1, const char *s2) {
int nw = (n + 63) >> 6;
for (int i = 0; i <= nw + 1; i++) { E0[i] = E1[i] = F0[i] = F1[i] = 0; }
{
/* Vectorised plane build. E0 = (a != '0'), E1 = (a == '2'),
F0 = (c != '1'), F1 = (c == '0') -- algebraically identical to the
branchy version, but with no data-dependent branches at all
(the scalar form mispredicts ~4x per 3 characters on random input). */
const __m256i Z0 = _mm256_set1_epi8('0'), Z1 = _mm256_set1_epi8('1'), Z2 = _mm256_set1_epi8('2');
int i = 0;
for (; i + 32 <= n; i += 32) {
int w = i >> 6, b = i & 63;
__m256i v1 = _mm256_loadu_si256((const __m256i *)(s1 + i));
__m256i v2 = _mm256_loadu_si256((const __m256i *)(s2 + i));
unsigned e0 = ~(unsigned)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, Z0));
unsigned e1 = (unsigned)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v1, Z2));
unsigned f0 = ~(unsigned)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v2, Z1));
unsigned f1 = (unsigned)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v2, Z0));
if (b == 0) { E0[w] = e0; E1[w] = e1; F0[w] = f0; F1[w] = f1; }
else { E0[w] |= (unsigned long long)e0 << 32; E1[w] |= (unsigned long long)e1 << 32;
F0[w] |= (unsigned long long)f0 << 32; F1[w] |= (unsigned long long)f1 << 32; }
}
for (; i < n; i++) {
int w = i >> 6, b = i & 63;
int a = s1[i] - '0', c = s2[i] - '0';
if (a >= 1) E0[w] |= 1ull << b;
if (a >= 2) E1[w] |= 1ull << b;
int g = (c + 2) % 3;
if (g != 0) F0[w] |= 1ull << b;
if (g >= 2) F1[w] |= 1ull << b;
}
}
g_nw = nw;
for (int i = 0; i < 64; i++) WMASK[i] = (1ull << i) - 1ull;
WMASK[64] = ~0ull;
for (int o = 0; o < 4; o++)
for (int lo = 0; lo < 64; lo++) {
unsigned long long *p = (unsigned long long *)&FMASK[o * 64 + lo];
for (int j = 0; j < 4; j++) p[j] = (j < o) ? 0ull : (j == o ? ~WMASK[lo] : ~0ull);
}
for (int o = 0; o < 4; o++)
for (int hh = 1; hh <= 64; hh++) {
unsigned long long *p = (unsigned long long *)&LMASK[o * 64 + hh - 1];
for (int j = 0; j < 4; j++) p[j] = (j < o) ? ~0ull : (j == o ? WMASK[hh] : 0ull);
}
}
static inline int have_avx2(void) {
unsigned a, b, c, d;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(1), "c"(0));
int osxsave = (int)((c >> 27) & 1u), avx = (int)((c >> 28) & 1u);
if (!(osxsave && avx)) return 0;
unsigned lo, hi;
__asm__ volatile("xgetbv" : "=a"(lo), "=d"(hi) : "c"(0));
if ((lo & 6u) != 6u) return 0;
__asm__ volatile("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(7), "c"(0));
return (int)((b >> 5) & 1u);
}
void solve(SOLVE_ARGS) {
build_all(n, s1, s2);
// group queries by the relative bit offset (sign aware), counting sort
for (int i = 0; i <= 512; i++) GHEAD[i] = 0;
for (int k = 0; k < q; k++) {
int d = q_y[k] - q_x[k];
int ad = (d >= 0 ? d : -d);
int g = ((d >= 0) ? 0 : 64) + (ad & 63);
GHEAD[g * 4 + ((ad >> 6) & 3) + 1]++;
}
for (int i = 0; i < 512; i++) GHEAD[i + 1] += GHEAD[i];
for (int i = 0; i < 512; i++) GCUR[i] = GHEAD[i];
for (int k = 0; k < q; k++) {
int x = q_x[k], y = q_y[k], l = q_len[k];
int d = y - x;
int ad = (d >= 0 ? d : -d);
int g = ((d >= 0) ? 0 : 64) + (ad & 63);
int p = GCUR[g * 4 + ((ad >> 6) & 3)]++;
ORD[p] = k;
RQ[p] = (unsigned long long)(unsigned)x | ((unsigned long long)(unsigned)y << 21)
| ((unsigned long long)(unsigned)l << 42);
}
#ifdef TESTSCALAR
run_scalar(n, q, q_x, q_y, q_len, ans);
#else
if (have_avx2()) run_avx2(n, q, q_x, q_y, q_len, ans);
else run_scalar(n, q, q_x, q_y, q_len, ans);
#endif
}
| Compilation | N/A | N/A | Compile OK | Score: N/A | 显示更多 |
| Testcase #1 | 3 s | 1027 MB + 624 KB | Time Limit Exceeded | Score: 0 | 显示更多 |
| Testcase #2 | 3 s | 1025 MB + 344 KB | Time Limit Exceeded | Score: 0 | 显示更多 |