Submission #1317364

#TimeUsernameProblemLanguageResultExecution timeMemory
1317364spetrNile (IOI24_nile)C++20
51 / 100
67 ms12948 KiB
#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define all(v) v.begin(), v.end()

// Struktura pro reprezentaci artefaktu po seřazení
struct Artifact {
    int id;
    int w, a, b;
    ll diff; // cena navíc za to, že je sám (A - B)
};

// Struktura pro DSU (Disjoint Set Union)
struct DSU {
    vector<int> parent;
    vector<int> sz; // velikost komponenty
    // min_p[0] ukládá min(A-B) pro prvky s globálním sudým indexem v této komponentě
    // min_p[1] ukládá min(A-B) pro prvky s globálním lichým indexem
    vector<ll> min_p[2]; 
    vector<bool> can_swap; // Existuje uvnitř komponenty hrana délky 2?
    
    // Globální součet penalizací (pro liché komponenty)
    ll total_penalty = 0;

    DSU(int n, const vector<Artifact>& arts) {
        parent.resize(n);
        iota(all(parent), 0);
        sz.assign(n, 1);
        min_p[0].assign(n, 2e18); // Inicializace nekonečnem
        min_p[1].assign(n, 2e18);
        can_swap.assign(n, false);

        for(int i=0; i<n; ++i) {
            min_p[i%2][i] = arts[i].diff; // Nastavíme počáteční diff
            total_penalty += arts[i].diff; // Na začátku jsou všichni sami (ostrůvky velikosti 1)
        }
    }

    int find(int i) {
        if (parent[i] == i)
            return i;
        return parent[i] = find(parent[i]);
    }

    // Pomocná funkce pro výpočet penalizace konkrétní komponenty (reprezentované kořenem root)
    ll get_component_penalty(int root) {
        if (sz[root] % 2 == 0) return 0; // Sudá délka -> všichni spárovaní, žádné penále
        
        // Lichá délka: Musíme nechat jednoho samotného.
        // Standardně musí mít stejnou paritu jako "většina" v komponentě.
        // Ale DSU neví, kde komponenta začíná, tak to určíme trikem:
        // V liché komponentě je počet sudých a lichých indexů různý. 
        // Ten typ indexu, kterého je víc, je "většinový".
        // Protože min_p[0] a min_p[1] drží globální parity, stačí porovnat, 
        // která parita má víc "zástupců"? Ne, to je složité sledovat.
        
        // Jednodušší: V liché komponentě o 1 prvku (index i) je "většina" parita (i%2).
        // Při spojování (SUDÁ + LICHÁ -> LICHÁ) se parita většiny nemění.
        // Při spojování (LICHÁ + LICHÁ -> SUDÁ) penále zmizí.
        // Takže nám stačí vědět, jakou paritu má "většina".
        // Ale my ani to nepotřebujeme. 
        // V liché komponentě existuje jedna parita, která má o 1 prvek víc.
        // Řekněme, že parita P je majoritní. Musíme obětovat někoho z P.
        // Pokud máme swap, můžeme obětovat někoho z !P.
        
        // Jak poznat majoritní paritu?
        // Při inicializaci (velikost 1, index i) je majorita (i%2).
        // Uložíme si typ majority do DSU?
        // Ne, stačí sledovat min_p.
        // Pro liché komponenty vždy platí: Jeden typ parity je "nucený" (ten co má víc prvků).
        // V našem kódu níže budeme merge provádět opatrně a paritu si odvodíme.
        // Vlastně: Artifacty jsou seřazené. Komponenta je vždy INTERVAL [L, R].
        // Pokud je délka liché, majoritní parita je L%2.
        
        // Ale DSU nedrží L a R. Ale my víme, že hrany jsou jen (i, i+1). 
        // Takže v každé komponentě je `min_index` ten L.
        // Můžeme si držet `min_index` v DSU.
        return 0; // Placeholder, logika je přímo v unite/activate
    }
    
    // Protože get_component_penalty je složitější implementovat čistě,
    // budeme udržovat `total_penalty` inkrementálně při operacích.
    // Abychom to zvládli, potřebujeme uvnitř DSU vědět, která parita je ta "hlavní".
    // Přidáme si do DSU `start_index`.
    vector<int> start_index; 
    // Pro konstruktor: start_index[i] = i;
    
    // Skutečná funkce pro výpočet penále:
    ll calc_penalty(int root) {
        if (sz[root] % 2 == 0) return 0;
        
        int majority_parity = start_index[root] % 2;
        ll cost = min_p[majority_parity][root]; // Cena když obětujeme někoho z majority
        
        if (can_swap[root]) {
            cost = min(cost, min_p[1 - majority_parity][root]);
        }
        return cost;
    }

    void unite(int i, int j) {
        int root_i = find(i);
        int root_j = find(j);
        if (root_i != root_j) {
            // Odečteme staré penále
            total_penalty -= calc_penalty(root_i);
            total_penalty -= calc_penalty(root_j);

            // Merge (union by size/random, here simply attach j to i)
            parent[root_j] = root_i;
            sz[root_i] += sz[root_j];
            
            // Update minim
            min_p[0][root_i] = min(min_p[0][root_i], min_p[0][root_j]);
            min_p[1][root_i] = min(min_p[1][root_i], min_p[1][root_j]);
            
            // Update swap a start index (start index je min z obou)
            can_swap[root_i] = can_swap[root_i] | can_swap[root_j];
            start_index[root_i] = min(start_index[root_i], start_index[root_j]);

            // Přičteme nové penále
            total_penalty += calc_penalty(root_i);
        }
    }

    void activate_swap(int i) {
        int root = find(i);
        if (!can_swap[root]) {
            total_penalty -= calc_penalty(root);
            can_swap[root] = true;
            total_penalty += calc_penalty(root);
        }
    }
};

std::vector<long long> calculate_costs(
    std::vector<int> W, std::vector<int> A, 
    std::vector<int> B, std::vector<int> E) {

    int n = W.size();
    vector<Artifact> arts(n);
    ll base_cost = 0; // Součet všech B (kdyby bylo vše spárováno)

    for(int i=0; i<n; ++i) {
        arts[i] = {i, W[i], A[i], B[i], (ll)A[i] - B[i]};
        base_cost += B[i];
    }
    sort(all(arts), [](const Artifact& a, const Artifact& b){
        return a.w < b.w;
    });

    // Eventy: {weight, type, index}
    // Type 1: Edge (i, i+1) -> weight = W[i+1] - W[i]
    // Type 2: Edge (i, i+2) -> weight = W[i+2] - W[i]
    // Type 3: Query -> weight = E[i]
    struct Event {
        int w, type, idx;
        bool operator<(const Event& other) const {
            if (w != other.w) return w < other.w;
            return type < other.type; // Nejdřív hrany (1,2), pak dotazy (3)
        }
    };

    vector<Event> events;
    for(int i=0; i < n-1; ++i) {
        events.push_back({arts[i+1].w - arts[i].w, 1, i});
    }
    for(int i=0; i < n-2; ++i) {
        events.push_back({arts[i+2].w - arts[i].w, 2, i});
    }
    for(int i=0; i < E.size(); ++i) {
        events.push_back({E[i], 3, i});
    }
    sort(all(events));

    // Inicializace DSU
    // Pozor: DSU potřebuje pracovat s indexy v poli `arts` (0..n-1), ne původními ID
    // Ale protože jsme sortovali `arts`, indexy 0..n-1 v DSU odpovídají seřazenému poli.
    // Musíme ale správně nastavit start_index
    DSU dsu(n, arts);
    dsu.start_index.resize(n);
    iota(all(dsu.start_index), 0);

    vector<ll> results(E.size());

    for(auto& ev : events) {
        if (ev.type == 1) {
            // Spojení i a i+1
            dsu.unite(ev.idx, ev.idx+1);
        } else if (ev.type == 2) {
            // Aktivace swapu pro komponentu obsahující i (a tím pádem i i+2)
            // Poznámka: Pokud hrana (i, i+2) je aktivní, pak (i, i+1) a (i+1, i+2)
            // jsou zaručeně taky aktivní (díky trojúhelníkové nerovnosti na 1D přímce).
            // Takže i a i+2 jsou už ve stejné komponentě.
            dsu.activate_swap(ev.idx);
        } else {
            // Query
            results[ev.idx] = base_cost + dsu.total_penalty;
        }
    }

    return results;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...