結果

問題 No.3677 Global Checksum
コンテスト
ユーザー harurun
提出日時 2026-09-03 16:19:16
言語 C++23(gcc16)
(gcc 16.1.0 + boost 1.92.0)
コンパイル:
g++-16 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
WA  
実行時間 -
コード長 13,918 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 1,199 ms
コンパイル使用メモリ 208,952 KB
実行使用メモリ 42,752 KB
最終ジャッジ日時 2026-09-04 23:12:29
合計ジャッジ時間 5,688 ms
ジャッジサーバーID
(参考情報)
judge3_0 / judge4_1
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample WA * 3
other WA * 20
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <immintrin.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include <cstdint>
#include <cstdlib>
#include <vector>

using u128 = unsigned __int128;

#if defined(__GNUC__)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
#else
#define UNLIKELY(x) (x)
#endif

[[noreturn]]
static void fail() {
    _exit(1);
}

static inline bool isDigit(char c) {
    return static_cast<unsigned char>(c - '0') <= 9;
}

static uint32_t readUInt(
    const char*& p,
    const char* end,
    uint32_t lo,
    uint32_t hi
) {
    if (UNLIKELY(p == end || !isDigit(*p))) {
        fail();
    }

    uint32_t x = 0;

    do {
        uint32_t d =
            static_cast<uint32_t>(*p - '0');

        if (UNLIKELY(x > (hi - d) / 10)) {
            fail();
        }

        x = x * 10 + d;
        ++p;
    } while (p != end && isDigit(*p));

    if (UNLIKELY(x < lo)) {
        fail();
    }

    return x;
}

static inline void readSpace(
    const char*& p,
    const char* end
) {
    if (UNLIKELY(p == end || *p != ' ')) {
        fail();
    }

    ++p;
}

static inline void readEoln(
    const char*& p,
    const char* end
) {
    if (UNLIKELY(p == end)) {
        fail();
    }

    if (*p == '\n') {
        ++p;
        return;
    }

    if (
        *p == '\r' &&
        p + 1 != end &&
        p[1] == '\n'
    ) {
        p += 2;
        return;
    }

    fail();
}

/*
    A の制約:
        0 <= A <= 1,000,000,000

    各整数を uint32_t に変換するのではなく、
    AVX-512 で 64 byte ずつ文字種を判定する。

    leading zero も許容する。

        000000000000001   -> OK
        0001000000000     -> OK (= 1e9)
        0001000000001     -> NG

    という判定も正しく行う。
*/
__attribute__((
    target("avx512f,avx512bw,bmi,bmi2,popcnt"),
    optimize("O3", "unroll-loops"),
    noinline
))
static void validateBody(
    const char* p,
    const char* end,
    int H,
    int W
) {
    const __m512i C0 =
        _mm512_set1_epi8('0');

    const __m512i C1 =
        _mm512_set1_epi8('1');

    const __m512i C10 =
        _mm512_set1_epi8(10);

    const __m512i CSPACE =
        _mm512_set1_epi8(' ');

    const __m512i CLF =
        _mm512_set1_epi8('\n');

    const __m512i CCR =
        _mm512_set1_epi8('\r');

    const char* rowBegin = p;

    uint64_t rowSpaces = 0;

    uint64_t crTotal = 0;
    uint64_t crUsed = 0;

    int rows = 0;

    /*
        block 境界をまたぐ整数に対応するため、
        直前 11 byte の情報を保持する。
    */
    uint64_t prevDigit = 0;
    uint64_t prevZero = 0;
    uint64_t prevOne = 0;

    bool prevSpace = false;

    while (p < end) {
        size_t rem =
            static_cast<size_t>(end - p);

        unsigned n =
            rem >= 64
                ? 64
                : static_cast<unsigned>(rem);

        uint64_t laneMask =
            n == 64
                ? ~0ULL
                : ((1ULL << n) - 1);

        __m512i v;

        if (n == 64) {
            v = _mm512_loadu_si512(
                reinterpret_cast<const void*>(p)
            );
        } else {
            v = _mm512_maskz_loadu_epi8(
                static_cast<__mmask64>(laneMask),
                p
            );
        }

        /*
            unsigned(c - '0') < 10
            で digit 判定。
        */
        __m512i sub =
            _mm512_sub_epi8(v, C0);

        uint64_t digit =
            static_cast<uint64_t>(
                _mm512_cmp_epu8_mask(
                    sub,
                    C10,
                    _MM_CMPINT_LT
                )
            ) & laneMask;

        uint64_t zero =
            static_cast<uint64_t>(
                _mm512_cmpeq_epi8_mask(v, C0)
            ) & laneMask;

        uint64_t one =
            static_cast<uint64_t>(
                _mm512_cmpeq_epi8_mask(v, C1)
            ) & laneMask;

        uint64_t space =
            static_cast<uint64_t>(
                _mm512_cmpeq_epi8_mask(
                    v,
                    CSPACE
                )
            ) & laneMask;

        uint64_t lf =
            static_cast<uint64_t>(
                _mm512_cmpeq_epi8_mask(
                    v,
                    CLF
                )
            ) & laneMask;

        uint64_t cr =
            static_cast<uint64_t>(
                _mm512_cmpeq_epi8_mask(
                    v,
                    CCR
                )
            ) & laneMask;

        uint64_t separators =
            space | lf | cr;

        /*
            数字 / space / LF / CR
            以外が含まれていたら NG。
        */
        if (UNLIKELY(
            (digit | separators) != laneMask
        )) {
            fail();
        }

        /*
            連続した space は NG。
        */
        if (UNLIKELY(
            space & (space << 1)
        )) {
            fail();
        }

        /*
            block 境界をまたぐ連続 space。
        */
        if (UNLIKELY(
            prevSpace && (space & 1)
        )) {
            fail();
        }

        /*
            直前11文字 + 現在の64文字を
            128-bit mask 上に並べる。

            bit 0..10  : 前 block の末尾11文字
            bit 11..74 : 現在の block
        */
        u128 D =
            (static_cast<u128>(digit) << 11)
            | prevDigit;

        u128 Z =
            (static_cast<u128>(zero) << 11)
            | prevZero;

        u128 O =
            (static_cast<u128>(one) << 11)
            | prevOne;

        u128 TERM =
            static_cast<u128>(separators) << 11;

        /*
            delimiter の直前10文字が
            全部数字かどうか。
        */
        u128 last10digits =
            (D << 1)
            & (D << 2)
            & (D << 3)
            & (D << 4)
            & (D << 5)
            & (D << 6)
            & (D << 7)
            & (D << 8)
            & (D << 9)
            & (D << 10);

        /*
            delimiter の11文字前が non-zero で、
            そこから delimiter まで11桁以上の
            数字が続いていれば、必ず 1e9 超過。

            leading zero はここでは無視されるため、

                000000000000000001

            のような値を誤って reject しない。
        */
        u128 tooLarge11 =
            TERM
            & last10digits
            & (
                static_cast<u128>(D & ~Z)
                << 11
            );

        if (UNLIKELY(tooLarge11 != 0)) {
            fail();
        }

        /*
            delimiter 直前9文字が数字。
        */
        u128 last9digits =
            (D << 1)
            & (D << 2)
            & (D << 3)
            & (D << 4)
            & (D << 5)
            & (D << 6)
            & (D << 7)
            & (D << 8)
            & (D << 9);

        /*
            significant digits が10桁あるケース。

            delimiter の10文字前が non-zero。
        */
        u128 exactly10sig =
            TERM
            & last9digits
            & (
                static_cast<u128>(D & ~Z)
                << 10
            );

        if (UNLIKELY(exactly10sig != 0)) {
            /*
                1e9 以下で10 significant digits
                なのは

                    1000000000

                だけ。
            */
            u128 last9zero =
                (Z << 1)
                & (Z << 2)
                & (Z << 3)
                & (Z << 4)
                & (Z << 5)
                & (Z << 6)
                & (Z << 7)
                & (Z << 8)
                & (Z << 9);

            u128 valid1e9 =
                (O << 10)
                & last9zero;

            if (UNLIKELY(
                exactly10sig & ~valid1e9
            )) {
                fail();
            }
        }

        /*
            CRLF validation 用。
        */
        crTotal +=
            static_cast<uint64_t>(
                __builtin_popcountll(cr)
            );

        /*
            行内の space 数を数える。

            space は1個ずつ処理せず、
            popcount だけで数える。
        */
        uint64_t remainingSpace = space;

        /*
            LF だけは行境界なので処理する。
            最大でも H <= 1,000,000 回。
        */
        uint64_t nl = lf;

        while (nl) {
            unsigned k =
                static_cast<unsigned>(
                    __builtin_ctzll(nl)
                );

            uint64_t lower =
                k == 0
                    ? 0
                    : ((1ULL << k) - 1);

            rowSpaces +=
                static_cast<uint64_t>(
                    __builtin_popcountll(
                        remainingSpace & lower
                    )
                );

            /*
                すでに数えた space を消す。
            */
            remainingSpace &= ~lower;

            const char* lineEnd = p + k;

            /*
                1行に W 個の整数なら、
                space は必ず W-1 個。
            */
            if (UNLIKELY(
                rowSpaces
                != static_cast<uint64_t>(W - 1)
            )) {
                fail();
            }

            /*
                空行禁止。
            */
            if (UNLIKELY(
                rowBegin == lineEnd
            )) {
                fail();
            }

            const char* dataEnd = lineEnd;

            /*
                CRLF の CR を除外。
            */
            if (
                dataEnd > rowBegin &&
                dataEnd[-1] == '\r'
            ) {
                ++crUsed;
                --dataEnd;
            }

            /*
                行頭・行末は数字でなければならない。

                これにより

                    " 1 2"
                    "1 2 "
                    "1 2 \r\n"

                なども reject する。
            */
            if (UNLIKELY(
                dataEnd == rowBegin
                || !isDigit(*rowBegin)
                || !isDigit(dataEnd[-1])
            )) {
                fail();
            }

            ++rows;

            if (UNLIKELY(rows > H)) {
                fail();
            }

            rowBegin = lineEnd + 1;

            rowSpaces = 0;

            nl &= nl - 1;
        }

        /*
            最後の LF より後の space を
            次 block / 次 LF まで持ち越す。
        */
        rowSpaces +=
            static_cast<uint64_t>(
                __builtin_popcountll(
                    remainingSpace
                )
            );

        /*
            次 block のために末尾11文字を保存。
        */
        if (n == 64) {
            prevDigit =
                (digit >> 53) & 0x7FF;

            prevZero =
                (zero >> 53) & 0x7FF;

            prevOne =
                (one >> 53) & 0x7FF;

            prevSpace =
                ((space >> 63) & 1) != 0;
        } else {
            prevSpace =
                ((space >> (n - 1)) & 1) != 0;
        }

        p += n;
    }

    /*
        行数。
    */
    if (UNLIKELY(rows != H)) {
        fail();
    }

    /*
        最終行にも EOL が必要。
        EOL 後に余計な文字があっても NG。
    */
    if (UNLIKELY(rowBegin != end)) {
        fail();
    }

    /*
        CR が存在するなら、すべて
        CRLF の CR でなければならない。
    */
    if (UNLIKELY(crTotal != crUsed)) {
        fail();
    }
}

static void validate(
    const char* begin,
    const char* end
) {
    const char* p = begin;

    int H =
        static_cast<int>(
            readUInt(
                p,
                end,
                1,
                1'000'000
            )
        );

    readSpace(p, end);

    int W =
        static_cast<int>(
            readUInt(
                p,
                end,
                1,
                4'000'000
            )
        );

    if (UNLIKELY(
        static_cast<int64_t>(H) * W
        > 4'000'000
    )) {
        fail();
    }

    readEoln(p, end);

    validateBody(
        p,
        end,
        H,
        W
    );
}

int main() {
    /*
        stdin が通常ファイルなら mmap。
        入力コピーを完全に省略する。
    */
    struct stat st{};

    if (
        fstat(STDIN_FILENO, &st) == 0 &&
        S_ISREG(st.st_mode)
    ) {
        size_t size =
            static_cast<size_t>(st.st_size);

        if (size == 0) {
            return 1;
        }

        void* mem =
            mmap(
                nullptr,
                size,
                PROT_READ,
                MAP_PRIVATE,
                STDIN_FILENO,
                0
            );

        if (mem != MAP_FAILED) {
            madvise(
                mem,
                size,
                MADV_SEQUENTIAL
            );

            const char* begin =
                static_cast<const char*>(mem);

            validate(
                begin,
                begin + size
            );

            return 0;
        }
    }

    /*
        stdin が pipe 等で mmap できない場合。
    */
    std::vector<char> buf;

    buf.reserve(64ULL << 20);

    constexpr size_t CHUNK = 1 << 20;

    char tmp[CHUNK];

    for (;;) {
        ssize_t n =
            read(
                STDIN_FILENO,
                tmp,
                sizeof(tmp)
            );

        if (n < 0) {
            return 1;
        }

        if (n == 0) {
            break;
        }

        buf.insert(
            buf.end(),
            tmp,
            tmp + n
        );
    }

    if (buf.empty()) {
        return 1;
    }

    validate(
        buf.data(),
        buf.data() + buf.size()
    );

    return 0;
}
0