제출 #1333156

#제출 시각아이디문제언어결과실행 시간메모리
1333156prunjuiceStone Arranging 2 (JOI23_ho_t1)C++20
100 / 100
215 ms45956 KiB
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// seg: start -> (end, color)
map<int, pair<int, ll>> seg;
// color -> set of (start, end) ordered by start
unordered_map<ll, set<pair<int,int>>> colorSegs;

// helper: add segment [l,r] with color c
void addSeg(int l, int r, ll c) {
    seg[l] = {r, c};
    colorSegs[c].insert({l, r});
}

// helper: erase segment by iterator in seg
void eraseSegByIt(map<int, pair<int,ll>>::iterator it) {
    int l = it->first;
    int r = it->second.first;
    ll c  = it->second.second;
    auto &S = colorSegs[c];
    S.erase({l, r});
    seg.erase(it);
}

// helper: find the segment containing position pos, or seg.end()
map<int, pair<int,ll>>::iterator findContaining(int pos) {
    auto it = seg.upper_bound(pos);
    if (it == seg.begin()) return seg.end();
    --it;
    int l = it->first;
    int r = it->second.first;
    if (l <= pos && pos <= r) return it;
    return seg.end();
}

// split segment that contains pos into [l,pos-1] and [pos,r]
// returns iterator to the segment that starts at pos (the right piece).
// if pos not inside any segment, returns seg.end()
// if pos already equal to segment start, returns iterator to that segment
map<int, pair<int,ll>>::iterator split(int pos) {
    auto it = findContaining(pos);
    if (it == seg.end()) return seg.end();
    int l = it->first;
    int r = it->second.first;
    ll c  = it->second.second;
    if (l == pos) return it;
    // split: remove old, insert left and right parts
    // erase from color set
    colorSegs[c].erase({l, r});
    seg.erase(it);
    // left part [l, pos-1]
    seg[l] = {pos - 1, c};
    colorSegs[c].insert({l, pos - 1});
    // right part [pos, r]
    seg[pos] = {r, c};
    colorSegs[c].insert({pos, r});
    return seg.find(pos);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N;
    if (!(cin >> N)) return 0;
    vector<ll> A(N+1);
    for (int i = 1; i <= N; ++i) cin >> A[i];

    // process stones 1..N
    for (int i = 1; i <= N; ++i) {
        ll c = A[i];

        // find rightmost occurrence j of color c among positions < i
        int j = -1;
        auto itColor = colorSegs.find(c);
        if (itColor != colorSegs.end() && !itColor->second.empty()) {
            // last element in set has largest start, its second is end
            auto last = *prev(itColor->second.end());
            j = last.second; // end index of last segment of color c
        }

        // if there's a previous occurrence, paint [j+1, i-1] to c
        if (j != -1 && j + 1 <= i - 1) {
            int L = j + 1, R = i - 1;
            // split on L and i (so boundaries align)
            auto itL = split(L);
            // split(i) might return seg.end() if i is outside current coverage,
            // which is fine. We want iterator to first start >= i as end boundary.
            split(i); // ensures any segment crossing i is cut so lower_bound(i) is correct

            auto itR = seg.lower_bound(i); // first segment start >= i
            // collect starts to delete between [itL, itR)
            vector<int> toDel;
            for (auto it = itL; it != itR; ++it) {
                toDel.push_back(it->first);
            }
            for (int s : toDel) {
                auto itRem = seg.find(s);
                if (itRem != seg.end()) eraseSegByIt(itRem);
            }
            // insert the combined new segment [L, R] with color c
            addSeg(L, R, c);
        }

        // insert single stone [i,i] with color c
        addSeg(i, i, c);

        // try to merge with previous
        auto it = seg.find(i);
        if (it != seg.begin()) {
            auto pit = it;
            --pit;
            // if adjacent and same color
            if (pit->second.first + 1 == it->first && pit->second.second == it->second.second) {
                int nl = pit->first;
                int nr = it->second.first;
                ll cc = it->second.second;
                // erase both
                eraseSegByIt(pit);
                eraseSegByIt(seg.find(i)); // it might be invalidated, find again by start nl? safer find
                // insert merged
                addSeg(nl, nr, cc);
                it = seg.find(nl);
            }
        }

        // try to merge with next
        if (!seg.empty()) {
            // refresh it (find the segment that contains i or that starts <= i)
            auto it2 = seg.upper_bound(i);
            if (it2 != seg.begin()) {
                --it2;
                if (it2->first <= i && it2->second.first >= i) {
                    auto cur = it2;
                    auto nit = next(cur);
                    if (nit != seg.end()) {
                        if (cur->second.first + 1 == nit->first && cur->second.second == nit->second.second) {
                            int nl = cur->first;
                            int nr = nit->second.first;
                            ll cc = cur->second.second;
                            eraseSegByIt(cur);
                            // after erasing cur, nit's iterator invalidated; find nit by start
                            eraseSegByIt(seg.find(nit->first));
                            addSeg(nl, nr, cc);
                        }
                    }
                }
            }
        }
    }

    // build final answer by iterating segments
    vector<ll> ans(N+1);
    for (auto &pr : seg) {
        int l = pr.first;
        int r = pr.second.first;
        ll c = pr.second.second;
        for (int i = l; i <= r; ++i) ans[i] = c;
    }

    // print
    for (int i = 1; i <= N; ++i) cout << ans[i] << "\n";
    return 0;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...