Submission #1316268

#TimeUsernameProblemLanguageResultExecution timeMemory
1316268turralRace (IOI11_race)C++20
0 / 100
0 ms332 KiB
#include "race.h"
#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

#define int long long
#define pi pair<int,int>

const int INF = 3e18;

// map -> length <-> min nºroads (ending  on our vertex i)
// int 1 -> we sum this num to all lengths (to let us use S2L)
// int 2 -> the same but to nºroads

int dfs_sz(int i, int p, vector<vector<pi> > & G, vector<int> & subTreeSz){
    int res=1;

    for (auto[j,w] : G[i]){
        if (j == p) continue;

        res += dfs_sz(j, i, G, subTreeSz);
    }

    return subTreeSz[i] = res;
}

void dfs(int i, int p, int & addLength, int & addRoads, int & res, unordered_map<int,int> & minRoads, vector<vector<pi> > & G, vector<int> & subTreeSz, int K){
    // find heavy child
    int mxSz=0, mxJ=-1, mxJW=0;
    for (auto [j, w] : G[i]){
        if (j != p && subTreeSz[j] > mxSz){
            mxSz = subTreeSz[j];
            mxJ = j;
            mxJW = w;
        }
    }

    if (mxJ == -1){
        // leaf: only zero-length path (ending at this vertex) with 0 roads
        minRoads.clear();
        minRoads[0] = 0;
        addLength = 0;
        addRoads = 0;
        return;
    }

    // process heavy child first (we'll keep its minRoads and offsets in-place)
    dfs(mxJ, i, addLength, addRoads, res, minRoads, G, subTreeSz, K);

    // move offsets up across edge (i <-> mxJ)
    addLength += mxJW;
    ++addRoads;

    // For every other child, process and merge into minRoads (which currently holds heavy child's data)
    for (auto [j, w] : G[i]){
        if (j == p || j == mxJ) continue;

        unordered_map<int, int> auxMinRoads;
        int auxAddL = 0, auxAddR = 0;
        dfs(j, i, auxAddL, auxAddR, res, auxMinRoads, G, subTreeSz, K);

        // For each real entry in auxMinRoads, convert to current coordinate and try to pair
        for (auto &entry : auxMinRoads){
            int stored_l_in_aux = entry.first;      // stored key in aux: real_len_aux - auxAddL
            int stored_cnt_in_aux = entry.second;   // stored val in aux: real_edges_aux - auxAddR

            // get real values for a node in aux subtree (including edge w from i to that subtree)
            int real_len_aux = stored_l_in_aux + auxAddL + w;      // l + auxAddL + w
            int real_edges_aux = stored_cnt_in_aux + auxAddR + 1; // cnt + auxAddR + 1

            if (real_len_aux > K) continue; // cannot contribute to K

            // in minRoads the stored key is: stored_key = real_len_other - addLength
            // we need real_len_other = K - real_len_aux
            int needed_real_other = (int)K - real_len_aux;
            int needed_stored_key = needed_real_other - addLength;

            auto it = minRoads.find(needed_stored_key);
            if (it != minRoads.end()){
                // it->second is stored value: stored_edges_other = real_edges_other - addRoads
                int real_edges_other = it->second + addRoads;
                int total_edges = real_edges_aux + real_edges_other;
                if (total_edges < res) res = total_edges;
            }
        }

        // After checking combinations, merge aux entries into minRoads (store them in current stored coordinate)
        for (auto &entry : auxMinRoads){
            int stored_l_in_aux = entry.first;
            int stored_cnt_in_aux = entry.second;

            int real_len_aux = stored_l_in_aux + auxAddL + w;      // actual length
            int real_edges_aux = stored_cnt_in_aux + auxAddR + 1; // actual edges

            if (real_len_aux > K) continue; // no need to store lengths > K

            int stored_key_in_current = real_len_aux - addLength;         // stored key relative to current addLength
            int stored_val_in_current = real_edges_aux - addRoads;        // stored val relative to current addRoads

            auto it = minRoads.find(stored_key_in_current);
            if (it == minRoads.end()){
                minRoads[stored_key_in_current] = stored_val_in_current;
            } else {
                if (stored_val_in_current < it->second) it->second = stored_val_in_current;
            }
        }
    }

    // Finally, check if any path entirely within the heavy side (i.e., using minRoads) gives length K
    int needed_stored = (int)K - addLength; // stored key corresponding to real length K
    auto itf = minRoads.find(needed_stored);
    if (itf != minRoads.end()){
        int real_edges = itf->second + addRoads;
        if (real_edges < res) res = real_edges;
    }
}

signed best_path(signed N, signed K, signed H[][2], signed L[]){
    vector<vector<pi> > G(N);
    for (int i=0; i<N-1; ++i){
        int u = H[i][0], v = H[i][1];
        int w = L[i];

        G[u].push_back(pi(v,w));
        G[v].push_back(pi(u,w));
    }

    vector<int> subTreeSz(N); 
    dfs_sz(0, -1, G, subTreeSz);

    unordered_map<int, int> minRoads;
    int addLength=0, addRoads=0;
    int res=INF;
    dfs(0, -1, addLength, addRoads, res, minRoads, G, subTreeSz, K);

    signed nres = (res>=INF ? -1 : res);
    return nres;
}

/*
signed main(){
    int N, K; cin >> N >> K;
    vector<vector<int> > H(N, vector<int>(2));
    vector<int> L(N);
    for (int i=0; i<N-1; ++i){
        cin >> H[i][0] >> H[i][1] >> L[i];
    }

    cout << best_path(N, K, H, L) << '\n';
    return 0;
}
*/
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...