Submission #1294972

#TimeUsernameProblemLanguageResultExecution timeMemory
1294972Davdav1232The Potion of Great Power (CEOI20_potion)C++20
Compilation error
0 ms0 KiB
#pragma GCC optimize("Ofast,unroll-loops")


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

const int MAX_N = 100000 + 5;
int H[MAX_N];
int N;
int D;
int d = 70; // block size; can tune (e.g. 300..700)

struct Snapshot {
    int day;               // time of snapshot (the last update index included)
    vector<int> items;     // neighbors present at this snapshot (no order assumptions)
};

vector<vector<Snapshot>> changes;               // per-node snapshots (starts with one empty snapshot)
vector<vector<pair<int,int>>> upd;              // per-node updates: pair<time, neighbor>

static int markStamp[MAX_N];                    // global marks (timestamping)
static int globalStamp = 1;

// initialize arrays and structures
inline void init(int NN, int DD, int HH[]) {
    N = NN;
    D = DD;
    for (int i = 0; i < N; ++i) H[i] = HH[i];

    changes.assign(N, vector<Snapshot>());
    upd.assign(N, vector<pair<int,int>>());

    for (int i = 0; i < N; ++i) {
        changes[i].push_back(Snapshot{0, vector<int>()});
    }

    // reset global stamping
    ++globalStamp;
    if (globalStamp > (1<<30)) { // safety wrap (unlikely)
        memset(markStamp, 0, sizeof(markStamp));
        globalStamp = 1;
    }
}

// add UU pairwise updates described by arrays AA[], BB[] (1-based times implied in original)
inline void curseChanges(int UU, int AA[], int BB[]) {
    // append updates to adjacency update lists
    for (int i = 0; i < UU; ++i) {
        int a = AA[i];
        int b = BB[i];
        int t = i + 1; // time index as in original code
        upd[a].push_back({t, b});
        upd[b].push_back({t, a});
    }

    // build block snapshots for each node
    for (int node = 0; node < N; ++node) {
        int usize = (int)upd[node].size();
        if (usize == 0) continue;
        // reserve approx number of snapshots
        int expected_blocks = (usize + d - 1) / d;
        changes[node].reserve(max(1, (int)changes[node].size() + expected_blocks + 2));

        // for each full block of size d, create snapshot
        for (int r = d; r <= usize; r += d) {
            const Snapshot &prev = changes[node].back();
            // build new snapshot by toggling the block [r-d, r-1] on top of prev.items
            ++globalStamp; // unique stamp for this construction
            int stamp = globalStamp;
            vector<int> touched;
            touched.reserve((int)prev.items.size() + d);

            // mark all prev items as present with stamp
            for (int v : prev.items) {
                markStamp[v] = stamp;
                touched.push_back(v);
            }

            // apply toggles from updates in this block
            for (int j = r - d; j < r; ++j) {
                int u = upd[node][j].second;
                if (markStamp[u] == stamp) {
                    // currently present -> toggle off: set to stamp-1 (absent)
                    markStamp[u] = stamp - 1;
                    // it's already in touched
                } else {
                    // not present -> toggle on
                    if (markStamp[u] != stamp - 1) { // first time we touch this element during this construction
                        touched.push_back(u);
                    }
                    markStamp[u] = stamp;
                }
            }

            // collect present items into new vector
            vector<int> newItems;
            newItems.reserve(touched.size());
            for (int v : touched) {
                if (markStamp[v] == stamp) newItems.push_back(v);
            }

            // push snapshot with the time of last update in that block
            int lastTime = upd[node][r - 1].first;
            changes[node].push_back(Snapshot{lastTime, std::move(newItems)});
        }
    }
}

// returns the list of neighbors of node p after applying all updates with time <= day
// complexity: O(size(snapshot) + number of partial updates applied)
inline vector<int> query(int p, int day) {
    const auto &snapVec = changes[p];
    int r = 0;
    for (int i = 0, sz = (int)snapVec.size(); i < sz; ++i) {
        if (snapVec[i].day <= day) r = i;
        else break;
    }

    // start from snapshot r
    const vector<int> &base = snapVec[r].items;
    int usize = (int)upd[p].size();
    int startIdx = r * d;

    ++globalStamp;
    int stamp = globalStamp;
    vector<int> touched;
    touched.reserve((int)base.size() + min(d, usize - startIdx));

    // mark base snapshot items as present
    for (int v : base) {
        markStamp[v] = stamp;
        touched.push_back(v);
    }

    // apply partial toggles from startIdx up to day
    for (int t = startIdx; t < usize && upd[p][t].first <= day; ++t) {
        int u = upd[p][t].second;
        if (markStamp[u] == stamp) {
            // present -> toggle off
            markStamp[u] = stamp - 1;
            // u is already in touched (either from base or previous toggle)
        } else {
            // not present -> toggle on
            if (markStamp[u] != stamp - 1) touched.push_back(u);
            markStamp[u] = stamp;
        }
    }

    // collect items that are present
    vector<int> ans;
    ans.reserve(touched.size());
    for (int v : touched) {
        if (markStamp[v] == stamp) ans.push_back(v);
    }
    return ans;
}

// compute minimal difference between H values in neighbors of x and y at time v
inline int question(int x, int y, int v) {
    vector<int> ans1 = query(x, v);
    vector<int> ans2 = query(y, v);

    if (ans1.empty() || ans2.empty()) return (int)1e9;

    vector<ll> h1; h1.reserve(ans1.size());
    vector<ll> h2; h2.reserve(ans2.size() + 2);

    for (int r : ans1) h1.push_back(H[r]);
    for (int r : ans2) h2.push_back(H[r]);

    // sentinels
    h2.push_back((ll)-1000000000);
    h2.push_back((ll)2000000000);

    sort(h1.begin(), h1.end());
    sort(h2.begin(), h2.end());

    ll m = (ll)4e18;
    // two-pointer-like approach
    int e = 0;
    for (int i = 0, n1 = (int)h1.size(); i < n1; ++i) {
        // advance e while next sentinel/value is < h1[i]
        while (e + 1 < (int)h2.size() && h2[e + 1] < h1[i]) ++e;
        // compare with nearest below and above
        if (e < (int)h2.size()) m = min(m, llabs(h1[i] - h2[e]));
        if (e + 1 < (int)h2.size()) m = min(m, llabs(h2[e + 1] - h1[i]));
    }
    if (m > (ll)2e9) return (int)1e9;
    return (int)m;
}

Compilation message (stderr)

/usr/bin/ld: /tmp/cc3XHRY7.o: in function `main':
grader.cpp:(.text.startup+0xe4): undefined reference to `init(int, int, int*)'
/usr/bin/ld: grader.cpp:(.text.startup+0x16f): undefined reference to `curseChanges(int, int*, int*)'
/usr/bin/ld: grader.cpp:(.text.startup+0x1cf): undefined reference to `question(int, int, int)'
collect2: error: ld returned 1 exit status