Submission #1275575

#TimeUsernameProblemLanguageResultExecution timeMemory
1275575mehrzadMigrations (IOI25_migrations)C++17
16 / 100
32 ms1336 KiB
#include "migrations.h"
#include <vector>
#include <algorithm>
#include <cmath>

namespace {
    // These variables will persist between calls to send_message
    int N_val;
    
    // Data structures for representing the tree and calculating LCA
    std::vector<int> parent;
    std::vector<int> depth;
    std::vector<std::vector<int>> up;
    int LOG_N;

    // Variables to track the current diameter
    int endpoint1 = 0, endpoint2 = 0;
    
    // Variables for the communication protocol
    int start_messaging_idx;
    bool init_sent = false;
    bool second_endpoint_pending = false;
    int pending_value = 0;
    int first_msg_idx = -1;
}

/**
 * @brief Initializes the data structures for LCA calculations.
 * This is called only once at the beginning of the simulation.
 * @param N The total number of sites.
 */
void init_lca(int N) {
    N_val = N;
    parent.assign(N, -1);
    depth.assign(N, 0);
    LOG_N = 0;
    while ((1 << LOG_N) <= N) {
        LOG_N++;
    }
    up.assign(N, std::vector<int>(LOG_N, 0));
    parent[0] = 0; // The root's parent is itself
    for(int k = 0; k < LOG_N; ++k) up[0][k] = 0;
}

/**
 * @brief Adds a new node to the tree structure for LCA calculations.
 * @param i The index of the new node.
 * @param p The index of its parent.
 */
void add_node_lca(int i, int p) {
    parent[i] = p;
    depth[i] = depth[p] + 1;
    up[i][0] = p;
    for (int k = 1; k < LOG_N; ++k) {
        up[i][k] = up[up[i][k-1]][k-1];
    }
}

/**
 * @brief Finds the Lowest Common Ancestor (LCA) of two nodes using binary lifting.
 * @param u First node.
 * @param v Second node.
 * @return The LCA of u and v.
 */
int get_lca(int u, int v) {
    if (depth[u] < depth[v]) std::swap(u, v);

    // Bring u to the same depth as v
    for (int k = LOG_N - 1; k >= 0; --k) {
        if ((depth[u] - (1 << k)) >= depth[v]) {
            u = up[u][k];
        }
    }

    if (u == v) return u;

    // Move u and v up together until their parents are the same
    for (int k = LOG_N - 1; k >= 0; --k) {
        if (up[u][k] != up[v][k]) {
            u = up[u][k];
            v = up[v][k];
        }
    }
    return parent[u];
}

/**
 * @brief Calculates the distance between two nodes in the tree.
 * @param u First node.
 * @param v Second node.
 * @return The distance between u and v.
 */
int get_dist(int u, int v) {
    if (u == -1 || v == -1) return -1;
    int lca = get_lca(u, v);
    return depth[u] + depth[v] - 2 * depth[lca];
}

/**
 * @brief  This function is called for each new site to determine if a message should be sent.
 * @param N The total number of sites.
 * @param i The index of the current site being added.
 * @param Pi The parent of the current site.
 * @return The message to be sent (0 if no message).
 */
int send_message(int N, int i, int Pi) {
    // Initialization on the first call
    if (i == 1) {
        init_lca(N);
        endpoint1 = 0;
        endpoint2 = 0;
        start_messaging_idx = std::max(1, N - 50);
        init_sent = false;
        second_endpoint_pending = false;
        pending_value = 0;
        first_msg_idx = -1;
    }
    
    // Add the new node to our tree representation
    add_node_lca(i, Pi);
    
    // Calculate the current diameter length
    int current_diameter_len = get_dist(endpoint1, endpoint2);
    
    // Calculate distances from the new node to the current diameter endpoints
    int dist_i_e1 = get_dist(i, endpoint1);
    int dist_i_e2 = get_dist(i, endpoint2);
    
    bool diameter_changed = false;
    int other_endpoint = -1;

    // Check if the new node extends the diameter
    if (dist_i_e1 > current_diameter_len || dist_i_e2 > current_diameter_len) {
        diameter_changed = true;
        if (dist_i_e1 > dist_i_e2) {
            other_endpoint = endpoint1;
            endpoint2 = i;
        } else {
            other_endpoint = endpoint2;
            endpoint1 = i;
        }
    }
    
    // We only send messages for the last 50 nodes to stay within the limit
    if (i < start_messaging_idx) {
        return 0;
    }

    int message_to_send = 0;
    
    // Logic for sending the initial diameter information
    if (!init_sent) {
        if (diameter_changed) {
            // If the diameter changes on our first messaging attempt, send the update
            message_to_send = other_endpoint + 1;
        } else {
            // Otherwise, send the first endpoint and queue the second
            message_to_send = endpoint1 + 1;
            first_msg_idx = i;
            second_endpoint_pending = true;
            pending_value = endpoint2 + 1;
        }
        init_sent = true;
    } else if (second_endpoint_pending) {
        // If we are waiting to send the second endpoint
        if (diameter_changed) {
            // An update takes precedence
            message_to_send = other_endpoint + 1;
        } else {
            // Send the queued second endpoint
            message_to_send = pending_value;
        }
        second_endpoint_pending = false;
    } else {
        // After initialization, only send messages on diameter changes
        if (diameter_changed) {
            message_to_send = other_endpoint + 1;
        }
    }
    
    return message_to_send;
}

/**
 * @brief This function is called once after all `send_message` calls.
 * It reconstructs the diameter from the received messages.
 * @param S A vector containing the messages sent. S[i] is the message from node i.
 * @return A pair of integers representing the endpoints of the diameter.
 */
std::pair<int, int> longest_path(std::vector<int> S) {
    std::vector<std::pair<int, int>> msgs;
    for (size_t i = 0; i < S.size(); ++i) {
        if (S[i] != 0) {
            msgs.push_back({(int)i, S[i]});
        }
    }
    
    // No messages, diameter is (0,0)
    if (msgs.empty()) {
        return {0, 0};
    }
    
    // Only one message, it's an update.
    if (msgs.size() == 1) {
        return {msgs[0].first, msgs[0].second - 1};
    }
    
    // Two messages, could be initialization or an update.
    if (msgs.size() == 2) {
        int i1 = msgs[0].first;
        int v1 = msgs[0].second;
        int i2 = msgs[1].first;
        int v2 = msgs[1].second;

        // Heuristic to differentiate: an initialization value for the second endpoint (v2-1)
        // cannot be equal to the index of the first message (i1), as it must be a node
        // that existed before i1.
        if (v2 == v1 || v2 - 1 == i1) {
             return {i2, v2 - 1};
        } else {
             return {v1 - 1, v2 - 1};
        }
    }
    
    // For more than two messages, the last one represents the final diameter.
    return {msgs.back().first, msgs.back().second - 1};
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...