Submission #403679

#TimeUsernameProblemLanguageResultExecution timeMemory
403679KoDCollapse (JOI18_collapse)C++17
100 / 100
3572 ms15836 KiB
#include <bits/stdc++.h>
#include "collapse.h"

using i32 = std::int32_t;
using u32 = std::uint32_t;
using i64 = std::int64_t;
using u64 = std::uint64_t;
using i128 = __int128_t;
using u128 = __uint128_t;
using isize = std::ptrdiff_t;
using usize = std::size_t;

class rep {
    struct Iter {
        usize itr;
        constexpr Iter(const usize pos) noexcept : itr(pos) {}
        constexpr void operator++() noexcept { ++itr; }
        constexpr bool operator!=(const Iter& other) const noexcept { return itr != other.itr; }
        constexpr usize operator*() const noexcept { return itr; }
    };
    const Iter first, last;

  public:
    explicit constexpr rep(const usize first, const usize last) noexcept : first(first), last(std::max(first, last)) {}
    constexpr Iter begin() const noexcept { return first; }
    constexpr Iter end() const noexcept { return last; }
};

class RollbackUnionFind {
    std::vector<usize> data;
    std::stack<std::pair<usize, usize>> history;

  public:
    explicit RollbackUnionFind(const usize size = 0) : data(size, -1), history() {}

    usize size() const { return data.size(); }

    usize leader(usize u) const {
        assert(u < size());
        while (data[u] < size()) u = data[u];
        return u;
    }

    usize size(const usize u) const {
        assert(u < size());
        return -data[leader(u)];
    }

    std::pair<usize, bool> merge(usize u, usize v) {
        assert(u < size());
        assert(v < size());
        u = leader(u);
        v = leader(v);
        if (u == v) return std::make_pair(u, false);
        if (data[u] > data[v]) std::swap(u, v);
        history.emplace(u, data[u]);
        history.emplace(v, data[v]);
        data[u] += data[v];
        data[v] = u;
        return std::make_pair(u, true);
    }

    bool same(const usize u, const usize v) const {
        assert(u < size());
        assert(v < size());
        return leader(u) == leader(v);
    }

    void rollback(const usize steps) {
        assert(2 * steps <= history.size());
        for (usize i = 2 * steps; i > 0; --i) {
            const auto [k, x] = history.top();
            history.pop();
            data[k] = x;
        }
    }
};

template <class T> using Vec = std::vector<T>;

constexpr usize BSIZE = 400;

Vec<int> calc(const usize N, Vec<int> T, Vec<int> X, Vec<int> Y, Vec<int> W, Vec<int> P) {
    const auto C = T.size();
    const auto Q = W.size();
    Vec<std::pair<int, int>> edge;
    edge.reserve(C);
    for (const auto i : rep(0, C)) {
        if (X[i] > Y[i]) {
            std::swap(X[i], Y[i]);
        }
        edge.emplace_back(Y[i], X[i]);
    }
    std::sort(edge.begin(), edge.end());
    edge.erase(std::unique(edge.begin(), edge.end()), edge.end());
    Vec<usize> eid(C);
    for (const auto i : rep(0, C)) {
        eid[i] = std::lower_bound(edge.begin(), edge.end(), std::make_pair(Y[i], X[i])) - edge.begin();
    }
    const auto Blocks = (C + BSIZE - 1) / BSIZE;
    Vec<Vec<usize>> qid(Blocks);
    for (const auto i : rep(0, Q)) {
        qid[W[i] / BSIZE].push_back(i);
    }
    Vec<int> ret(Q);
    for (const auto block : rep(0, Blocks)) {
        const auto low = BSIZE * block;
        const auto high = std::min(C, low + BSIZE);
        auto& qs = qid[block];
        std::sort(qs.begin(), qs.end(), [&](const usize i, const usize j) { return P[i] < P[j]; });
        Vec<char> usage(edge.size()), changes(edge.size());
        for (const auto i : rep(0, low)) {
            usage[eid[i]] ^= 1;
        }
        for (const auto i : rep(low, high)) {
            changes[eid[i]] = true;
        }
        Vec<usize> naive;
        for (const auto i : rep(0, edge.size())) {
            if (changes[i]) {
                naive.push_back(i);
            }
        }
        RollbackUnionFind dsu(N);
        usize comps = N, seen = 0;
        for (const auto i : qs) {
            while (seen < edge.size() and edge[seen].first <= P[i]) {
                if (!changes[seen] and usage[seen]) {
                    comps -= dsu.merge(edge[seen].first, edge[seen].second).second;
                }
                seen += 1;
            }
            const auto memo = comps;
            for (const auto j : rep(low, W[i] + 1)) {
                usage[eid[j]] ^= 1;
            }
            for (const auto e : naive) {
                if (usage[e] and edge[e].first <= P[i]) {
                    comps -= dsu.merge(edge[e].first, edge[e].second).second;
                }
            }
            for (const auto j : rep(low, W[i] + 1)) {
                usage[eid[j]] ^= 1;
            }
            ret[i] = comps;
            dsu.rollback(memo - comps);
            comps = memo;
        }
    }
    return ret;
}

Vec<int> simulateCollapse(int N, Vec<int> T, Vec<int> X, Vec<int> Y, Vec<int> W, Vec<int> P) {
    const auto C = T.size();
    const auto Q = W.size();
    auto ret = calc(N, T, X, Y, W, P);
    for (const auto i : rep(0, C)) {
        X[i] = N - X[i] - 1;
        Y[i] = N - Y[i] - 1;
    }
    for (const auto i : rep(0, Q)) {
        P[i] = N - P[i] - 2;
    }
    const auto tmp = calc(N, T, X, Y, W, P);
    for (const auto i : rep(0, Q)) {
        ret[i] += tmp[i];
        ret[i] -= N;
    }
    return ret;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...