結果

問題 No.3677 Global Checksum
コンテスト
ユーザー harurun
提出日時 2026-09-03 16:14:51
言語 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,371 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 1,452 ms
コンパイル使用メモリ 210,320 KB
実行使用メモリ 30,080 KB
最終ジャッジ日時 2026-09-04 23:12:12
合計ジャッジ時間 4,272 ms
ジャッジサーバーID
(参考情報)
judge1_0 / judge6_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample WA * 3
other WA * 12 TLE * 1 -- * 7
権限があれば一括ダウンロードができます

ソースコード

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)
#define TARGET_AVX512 \
    __attribute__((target("avx512f,avx512bw,bmi,bmi2,popcnt")))
#else
#define UNLIKELY(x) (x)
#define TARGET_AVX512
#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 void readSpace(
    const char*& p,
    const char* end
) {
    if (UNLIKELY(p == end || *p != ' ')) {
        fail();
    }
    ++p;
}

static 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 <= 1000000000

    数値変換はしない。

    token が
      0~9桁  -> OK
      10桁    -> 先頭 0、または 1000000000
      11桁~  -> NG

    10桁判定も 64 個まとめて bit 演算する。
*/
TARGET_AVX512
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;

    /*
        直前 11 byte 分を保持。

        bit 0..10:
            現在の 64 byte 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;

        if (n == 64) {
            laneMask = ~0ULL;
        } else {
            laneMask = (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
            );
        }

        /*
            digit 判定。

            unsigned(v - '0') < 10
        */
        __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;

        /*
            数字 / ' ' / '\n' / '\r'
            以外は即 NG
        */
        if (UNLIKELY(
            (digit | separators) != laneMask
        )) {
            fail();
        }

        /*
            連続スペースは NG。

            "1  2"
        */
        if (UNLIKELY(space & (space << 1))) {
            fail();
        }

        if (UNLIKELY(
            prevSpace && (space & 1)
        )) {
            fail();
        }

        /*
            ------------------------------
            A <= 1,000,000,000 の SIMD 検査
            ------------------------------
        */

        /*
            直前11 byte + 現在64 byte
            = 75 bit
        */
        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 ten =
            (D << 1)
            & (D << 2)
            & (D << 3)
            & (D << 4)
            & (D << 5)
            & (D << 6)
            & (D << 7)
            & (D << 8)
            & (D << 9)
            & (D << 10);

        /*
            直前11文字も数字なら
            11桁以上なので NG。
        */
        u128 tooLong =
            TERM
            & ten
            & (D << 11);

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

        /*
            ちょうど10桁の token。
        */
        u128 exactly10 =
            TERM
            & ten
            & ~(D << 11);

        if (exactly10 != 0) {
            /*
                token の後ろ9桁が
                すべて '0'
            */
            u128 nineZero =
                (Z << 1)
                & (Z << 2)
                & (Z << 3)
                & (Z << 4)
                & (Z << 5)
                & (Z << 6)
                & (Z << 7)
                & (Z << 8)
                & (Z << 9);

            /*
                10桁で valid なのは

                0xxxxxxxxx
                    または
                1000000000

                先頭 0 なら数値として
                1e9 未満なので OK。
            */
            u128 valid10 =
                (Z << 10)
                |
                (
                    (O << 10)
                    & nineZero
                );

            if (UNLIKELY(
                exactly10 & ~valid10
            )) {
                fail();
            }
        }

        /*
            CR の総数だけ数えておく。

            LF 処理時に CRLF として
            使用された CR の数と比較する。
        */
        crTotal +=
            static_cast<uint64_t>(
                __builtin_popcountll(cr)
            );

        /*
            ------------------------------
            行構造の検査
            ------------------------------

            space 自体は popcount するだけ。
            1個ずつ処理しない。

            LF だけ scalar で処理する。
        */
        uint64_t remainingSpace = space;
        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
                    )
                );

            remainingSpace &= ~lower;

            const char* lineEnd = p + k;

            /*
                この行に W-1 個の空白が
                ちょうど存在する必要がある。
            */
            if (UNLIKELY(
                rowSpaces
                != static_cast<uint64_t>(W - 1)
            )) {
                fail();
            }

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

            const char* dataEnd = lineEnd;

            /*
                CRLF
            */
            if (
                dataEnd > rowBegin &&
                dataEnd[-1] == '\r'
            ) {
                ++crUsed;
                --dataEnd;
            }

            /*
                行の先頭・末尾は数字。
            */
            if (UNLIKELY(
                dataEnd == rowBegin
                || !isDigit(*rowBegin)
                || !isDigit(dataEnd[-1])
            )) {
                fail();
            }

            ++rows;

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

            rowBegin = lineEnd + 1;
            rowSpaces = 0;

            nl &= nl - 1;
        }

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

        /*
            末尾11byteを次 block に渡す。
        */
        if (n == 64) {
            prevDigit =
                (digit >> 53) & 0x7FF;

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

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

            prevSpace =
                ((space >> 63) & 1) != 0;
        } else {
            /*
                最終 block なので
                実際には次回使用されない。
            */
            prevSpace =
                ((space >> (n - 1)) & 1) != 0;
        }

        p += n;
    }

    /*
        行数が H でなければ NG。
    */
    if (UNLIKELY(rows != H)) {
        fail();
    }

    /*
        最終行には必ず EOL が必要。
    */
    if (UNLIKELY(rowBegin != end)) {
        fail();
    }

    /*
        全 CR が CRLF の一部である必要がある。
    */
    if (UNLIKELY(crTotal != crUsed)) {
        fail();
    }
}

int main() {
    struct stat st;

    if (
        fstat(STDIN_FILENO, &st) != 0 ||
        !S_ISREG(st.st_mode)
    ) {
        /*
            pipe fallback
        */
        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;
        }

        const char* p = buf.data();
        const char* end = p + buf.size();

        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
        );

        return 0;
    }

    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) {
        return 1;
    }

    /*
        sequential access のヒント。
    */
    madvise(
        mem,
        size,
        MADV_SEQUENTIAL
    );

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

    const char* end =
        p + size;

    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
    );

    return 0;
}
0