Submission #1230652

#TimeUsernameProblemLanguageResultExecution timeMemory
1230652jajskaoColouring a rectangle (eJOI19_colouring)C++20
60 / 100
225 ms167936 KiB
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <map>

using namespace std;

// Use a large enough value for infinity in the context of the problem's costs.
const long long INF = 4e14; // Sum of all possible costs is approx 2e5*2*1e9 = 4e14

struct Edge {
    int to;
    long long capacity;
    int rev;
};

vector<vector<Edge>> adj;
vector<int> level;
vector<int> iter;

// Adds a directed edge from u to v with a certain capacity.
void add_edge(int u, int v, long long cap) {
    adj[u].push_back({v, cap, (int)adj[v].size()});
    adj[v].push_back({u, 0, (int)adj[u].size() - 1}); // Residual edge
}

// Builds the level graph using BFS for Dinic's algorithm.
bool bfs(int s, int t) {
    level.assign(adj.size(), -1);
    vector<int> q;
    q.push_back(s);
    level[s] = 0;
    int head = 0;
    while(head < q.size()){
        int u = q[head++];
        for(const auto& edge : adj[u]){
            if(edge.capacity > 0 && level[edge.to] < 0){
                level[edge.to] = level[u] + 1;
                q.push_back(edge.to);
            }
        }
    }
    return level[t] != -1;
}

// Finds an augmenting path using DFS on the level graph.
long long dfs(int u, int t, long long f) {
    if (u == t) return f;
    for (int& i = iter[u]; i < adj[u].size(); ++i) {
        Edge& e = adj[u][i];
        if (e.capacity > 0 && level[u] < level[e.to]) {
            long long d = dfs(e.to, t, min(f, e.capacity));
            if (d > 0) {
                e.capacity -= d;
                adj[e.to][e.rev].capacity += d;
                return d;
            }
        }
    }
    return 0;
}

// Computes the maximum flow from s to t using Dinic's algorithm.
long long max_flow(int s, int t) {
    long long flow = 0;
    while (bfs(s, t)) {
        iter.assign(adj.size(), 0);
        long long f;
        while ((f = dfs(s, t, INF)) > 0) {
            flow += f;
        }
    }
    return flow;
}

// Globals to be used by the segment tree functions
int m, n;
vector<int> lr_node_indices_global;
int seg_tree_nodes_start_global;

// Recursively builds the segment tree for min-cut graph optimization.
void build_seg_tree(int u, int l, int r) {
    if (l == r) {
        add_edge(seg_tree_nodes_start_global + u, lr_node_indices_global[l], INF);
        return;
    }
    int mid = l + (r - l) / 2;
    // Edges from parent to children in the segment tree
    add_edge(seg_tree_nodes_start_global + u, seg_tree_nodes_start_global + 2 * u, INF);
    add_edge(seg_tree_nodes_start_global + u, seg_tree_nodes_start_global + 2 * u + 1, INF);
    build_seg_tree(2 * u, l, mid);
    build_seg_tree(2 * u + 1, mid + 1, r);
}

// Adds edges from a source node to the segment tree nodes covering a target range.
void add_range_edges(int u, int l, int r, int target_l, int target_r, int from_node) {
    if (r < target_l || l > target_r) return;
    if (target_l <= l && r <= target_r) {
        add_edge(from_node, seg_tree_nodes_start_global + u, INF);
        return;
    }
    int mid = l + (r - l) / 2;
    add_range_edges(2 * u, l, mid, target_l, target_r, from_node);
    add_range_edges(2 * u + 1, mid + 1, r, target_l, target_r, from_node);
}

// Solves one of the two independent subproblems (even or odd parity).
long long solve_subproblem(const vector<pair<int, int>>& lr_diags, const vector<pair<int, int>>& rl_diags) {
    if (lr_diags.empty() || rl_diags.empty()) {
        return 0;
    }

    int p = lr_diags.size();
    int q = rl_diags.size();

    map<int, int> lr_id_to_idx;
    for (int i = 0; i < p; ++i) {
        lr_id_to_idx[lr_diags[i].first] = i;
    }
    
    // Define node indices for the graph
    int s = 0;
    int rl_nodes_start = 1;
    int lr_nodes_raw_start = rl_nodes_start + q;
    seg_tree_nodes_start_global = lr_nodes_raw_start + p;
    int seg_tree_size = 4 * p;
    int t = seg_tree_nodes_start_global + seg_tree_size;
    int num_nodes = t + 1;

    adj.assign(num_nodes, vector<Edge>());
    
    // Set up global variables for segment tree construction
    lr_node_indices_global.resize(p);
    iota(lr_node_indices_global.begin(), lr_node_indices_global.end(), lr_nodes_raw_start);
    
    // Add edges from source to RL-diagonal nodes
    for (int i = 0; i < q; ++i) {
        add_edge(s, rl_nodes_start + i, rl_diags[i].second);
    }

    // Add edges from LR-diagonal nodes to sink
    for (int i = 0; i < p; ++i) {
        add_edge(lr_nodes_raw_start + i, t, lr_diags[i].second);
    }
    
    // Build the segment tree over LR-diagonal nodes
    if (p > 0) {
        build_seg_tree(1, 0, p - 1);
    }

    // Connect RL-diagonal nodes to the segment tree
    for (int i = 0; i < q; ++i) {
        int d_rl = rl_diags[i].first;
        
        long long drl_ll = d_rl;
        long long n_ll = n;
        long long m_ll = m;
        
        // Calculate the range of LR-diagonal IDs that this RL-diagonal intersects
        long long d_lr_min = max(-drl_ll, drl_ll - 2 * n_ll + 2);
        long long d_lr_max = min(drl_ll, 2 * m_ll - 2 - drl_ll);

        auto it_min = lr_id_to_idx.lower_bound(d_lr_min);
        if (it_min == lr_id_to_idx.end() || it_min->first > d_lr_max) continue;
        
        auto it_max = lr_id_to_idx.upper_bound(d_lr_max);
        it_max--;
        
        int idx_min = it_min->second;
        int idx_max = it_max->second;
        
        if (idx_min > idx_max) continue;

        add_range_edges(1, 0, p - 1, idx_min, idx_max, rl_nodes_start + i);
    }

    return max_flow(s, t);
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> m >> n;

    vector<int> lr_costs(m + n - 1);
    vector<int> rl_costs(m + n - 1);

    for (int i = 0; i < m + n - 1; ++i) cin >> lr_costs[i];
    for (int i = 0; i < m + n - 1; ++i) cin >> rl_costs[i];

    vector<pair<int, int>> lr_even, lr_odd;
    vector<pair<int, int>> rl_even, rl_odd;

    // Partition diagonals by parity
    for (int i = 0; i < m + n - 1; ++i) {
        int id = i + 1 - n; // As per problem statement indexing
        if (abs(id) % 2 == 0) {
            lr_even.push_back({id, lr_costs[i]});
        } else {
            lr_odd.push_back({id, lr_costs[i]});
        }
    }

    for (int i = 0; i < m + n - 1; ++i) {
        int id = i; // As per problem statement indexing
        if (id % 2 == 0) {
            rl_even.push_back({id, rl_costs[i]});
        } else {
            rl_odd.push_back({id, rl_costs[i]});
        }
    }

    long long total_cost = 0;
    total_cost += solve_subproblem(lr_even, rl_even);
    total_cost += solve_subproblem(lr_odd, rl_odd);

    cout << total_cost << endl;

    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...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...