結果

問題 No.3671 Reusable Lazy Segment Tree
コンテスト
ユーザー harurun
提出日時 2026-08-05 17:09:55
言語 C++23(gcc16)
(gcc 16.1.0 + boost 1.92.0 + ACL)
コンパイル:
g++-16 -O2 -lm -std=c++23 -Wuninitialized -DONLINE_JUDGE -o a.out _filename_
実行:
./a.out
結果
AC  
実行時間 3,812 ms / 6,000 ms
+ 994µs
コード長 8,541 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 2,685 ms
コンパイル使用メモリ 363,152 KB
実行使用メモリ 60,416 KB
最終ジャッジ日時 2026-09-04 22:05:01
合計ジャッジ時間 25,740 ms
ジャッジサーバーID
(参考情報)
judge1_0 / judge2_0
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 1
other AC * 19
権限があれば一括ダウンロードができます

ソースコード

diff #
raw source code

#include <bits/stdc++.h>
using namespace std;

using u32 = uint32_t;
using u64 = uint64_t;

constexpr u32 FULL = (1u << 30) - 1;

struct Node {
    u64 sum = 0;
    u32 cnt[30]{};

    // 子に対して保留されている作用:
    // v -> (v & lazyAnd) | lazyOr
    u32 lazyAnd = FULL;
    u32 lazyOr = 0;
};

struct Backup {
    int index;
    Node node;
};

int N, M;

vector<Node> seg;
vector<int> lastModified;
vector<Backup> history;

int currentVersion = 0;

void saveNode(int p) {
    if (lastModified[p] == currentVersion) {
        return;
    }

    lastModified[p] = currentVersion;
    history.push_back({p, seg[p]});
}

void build(
    int p,
    int left,
    int right,
    const vector<u32>& A
) {
    seg[p].lazyAnd = FULL;
    seg[p].lazyOr = 0;

    if (left == right) {
        seg[p].sum = A[left];

        for (int bit = 0; bit < 30; ++bit) {
            seg[p].cnt[bit] = (A[left] >> bit) & 1u;
        }
        return;
    }

    int middle = (left + right) / 2;

    build(p * 2, left, middle, A);
    build(p * 2 + 1, middle + 1, right, A);

    seg[p].sum = seg[p * 2].sum + seg[p * 2 + 1].sum;

    for (int bit = 0; bit < 30; ++bit) {
        seg[p].cnt[bit] =
            seg[p * 2].cnt[bit]
            + seg[p * 2 + 1].cnt[bit];
    }
}

void applyOr(int p, u32 mask, int length) {
    u32 bits = mask;

    while (bits != 0) {
        int bit = __builtin_ctz(bits);
        bits &= bits - 1;

        u32 oldCount = seg[p].cnt[bit];

        if (oldCount != static_cast<u32>(length)) {
            seg[p].sum +=
                static_cast<u64>(length - oldCount) << bit;

            seg[p].cnt[bit] = length;
        }
    }

    seg[p].lazyOr |= mask;
}

void applyAnd(int p, u32 mask) {
    u32 bits = FULL ^ mask;

    while (bits != 0) {
        int bit = __builtin_ctz(bits);
        bits &= bits - 1;

        u32 oldCount = seg[p].cnt[bit];

        if (oldCount != 0) {
            seg[p].sum -= static_cast<u64>(oldCount) << bit;
            seg[p].cnt[bit] = 0;
        }
    }

    seg[p].lazyAnd &= mask;
    seg[p].lazyOr &= mask;
}

// 現在の値に
// v -> (v & andMask) | orMask
// を作用させる。
void applyTransform(
    int p,
    u32 andMask,
    u32 orMask,
    int length
) {
    // 強制的に 0 になるビット
    u32 clearBits = (FULL ^ andMask) & (FULL ^ orMask);

    while (clearBits != 0) {
        int bit = __builtin_ctz(clearBits);
        clearBits &= clearBits - 1;

        u32 oldCount = seg[p].cnt[bit];

        if (oldCount != 0) {
            seg[p].sum -= static_cast<u64>(oldCount) << bit;
            seg[p].cnt[bit] = 0;
        }
    }

    // 強制的に 1 になるビット
    u32 setBits = orMask;

    while (setBits != 0) {
        int bit = __builtin_ctz(setBits);
        setBits &= setBits - 1;

        u32 oldCount = seg[p].cnt[bit];

        if (oldCount != static_cast<u32>(length)) {
            seg[p].sum +=
                static_cast<u64>(length - oldCount) << bit;

            seg[p].cnt[bit] = length;
        }
    }

    // 新しい作用を、既存の遅延作用の後に合成する。
    seg[p].lazyAnd &= andMask;
    seg[p].lazyOr =
        (seg[p].lazyOr & andMask) | orMask;
}

void push(int p, int left, int right) {
    if (
        seg[p].lazyAnd == FULL
        && seg[p].lazyOr == 0
    ) {
        return;
    }

    saveNode(p);

    int middle = (left + right) / 2;
    int leftChild = p * 2;
    int rightChild = p * 2 + 1;

    saveNode(leftChild);
    saveNode(rightChild);

    u32 andMask = seg[p].lazyAnd;
    u32 orMask = seg[p].lazyOr;

    applyTransform(
        leftChild,
        andMask,
        orMask,
        middle - left + 1
    );

    applyTransform(
        rightChild,
        andMask,
        orMask,
        right - middle
    );

    seg[p].lazyAnd = FULL;
    seg[p].lazyOr = 0;
}

void update(
    int p,
    int left,
    int right,
    int queryLeft,
    int queryRight,
    bool isOr,
    u32 mask,
    u32 affectedBits
) {
    saveNode(p);

    if (queryLeft <= left && right <= queryRight) {
        if (isOr) {
            applyOr(p, mask, right - left + 1);
        } else {
            applyAnd(p, mask);
        }
        return;
    }

    push(p, left, right);

    int middle = (left + right) / 2;

    if (queryLeft <= middle) {
        update(
            p * 2,
            left,
            middle,
            queryLeft,
            queryRight,
            isOr,
            mask,
            affectedBits
        );
    }

    if (middle < queryRight) {
        update(
            p * 2 + 1,
            middle + 1,
            right,
            queryLeft,
            queryRight,
            isOr,
            mask,
            affectedBits
        );
    }

    seg[p].sum =
        seg[p * 2].sum
        + seg[p * 2 + 1].sum;

    // OR なら mask のビット、
    // AND なら mask が 0 のビットしか変化しない。
    u32 bits = affectedBits;

    while (bits != 0) {
        int bit = __builtin_ctz(bits);
        bits &= bits - 1;

        seg[p].cnt[bit] =
            seg[p * 2].cnt[bit]
            + seg[p * 2 + 1].cnt[bit];
    }
}

u64 rangeSum(
    int p,
    int left,
    int right,
    int queryLeft,
    int queryRight
) {
    if (queryLeft <= left && right <= queryRight) {
        return seg[p].sum;
    }

    push(p, left, right);

    int middle = (left + right) / 2;
    u64 answer = 0;

    if (queryLeft <= middle) {
        answer += rangeSum(
            p * 2,
            left,
            middle,
            queryLeft,
            queryRight
        );
    }

    if (middle < queryRight) {
        answer += rangeSum(
            p * 2 + 1,
            middle + 1,
            right,
            queryLeft,
            queryRight
        );
    }

    return answer;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> N >> M;

    vector<u32> A(N + 1);

    for (int i = 1; i <= N; ++i) {
        cin >> A[i];
    }

    vector<u32> l(M + 1);
    vector<u32> r(M + 1);
    vector<u32> x(M + 1);
    vector<u32> L(M + 1);
    vector<u32> R(M + 1);

    for (int i = 1; i <= M; ++i) {
        cin >> l[i];
    }

    for (int i = 1; i <= M; ++i) {
        cin >> r[i];
    }

    for (int i = 1; i <= M; ++i) {
        cin >> x[i];
    }

    for (int i = 1; i <= M; ++i) {
        cin >> L[i];
    }

    for (int i = 1; i <= M; ++i) {
        cin >> R[i];
    }

    seg.resize(4 * N + 5);
    lastModified.assign(4 * N + 5, 0);

    build(1, 1, N, A);

    int Q;
    cin >> Q;

    history.reserve(200000);

    for (int problemIndex = 1; problemIndex <= Q; ++problemIndex) {
        int s, q;
        cin >> s >> q;

        ++currentVersion;
        history.clear();

        u32 y = problemIndex;

        auto clampIndex = [&](u32 value) -> int {
            if (value == 0) {
                return 1;
            }

            if (value > static_cast<u32>(N)) {
                return N;
            }

            return static_cast<int>(value);
        };

        for (int j = 1; j <= q; ++j) {
            int z = (s + j) % M + 1;

            int u = clampIndex(l[z] ^ y);
            int v = clampIndex(r[z] ^ y);

            int updateLeft = min(u, v);
            int updateRight = max(u, v);

            int upperU = clampIndex(L[z] ^ y);
            int upperV = clampIndex(R[z] ^ y);

            int sumLeft = min(upperU, upperV);
            int sumRight = max(upperU, upperV);

            u32 mask = x[z] ^ y;

            if (z % 2 == 0) {
                update(
                    1,
                    1,
                    N,
                    updateLeft,
                    updateRight,
                    true,
                    mask,
                    mask
                );
            } else {
                update(
                    1,
                    1,
                    N,
                    updateLeft,
                    updateRight,
                    false,
                    mask,
                    FULL ^ mask
                );
            }

            y = static_cast<u32>(
                rangeSum(
                    1,
                    1,
                    N,
                    sumLeft,
                    sumRight
                ) & FULL
            );
        }

        cout << y << '\n';

        // 配列を小問題開始前の状態に戻す。
        for (const Backup& backup : history) {
            seg[backup.index] = backup.node;
        }
    }

    return 0;
}
0