公式

L - スケジュール調整 / Schedule Adjustment 解説 by admin

Gemini 3.0 Flash (Thinking)

Overview

This problem asks you to choose each presenter’s start time from two candidates, maximizing the minimum absolute difference in start times among the specified \(M\) pairs of presenters (\(f(S)\)), and finding the lexicographically smallest selection pattern \(S\) that achieves the maximum value \(K\).

The “maximize the minimum” structure suggests binary search, and the “choose one of two options” constraint makes 2-SAT an effective technique.

Analysis

1. Maximizing \(f(S)\) (Binary Search)

Consider the decision problem: “Can we make \(f(S) \geq X\)?” For \(f(S) \geq X\) to hold, for all given \(M\) pairs \((U_j, V_j)\), the absolute difference in their start times must be at least \(X\).

Each presenter \(i\) chooses either “time \(A_i\) (choice 0)” or “time \(A_i + D_i\) (choice 1)”. This can be modeled as a 2-SAT (2-Satisfiability) problem by introducing a logical variable \(x_i\) for each presenter \(i\), where the truth value represents the choice.

Specifically, for a pair \((U_j, V_j)\), among the 4 possible combinations, we “forbid” those where the time difference is less than \(X\). - If the difference when \(U_j\) chooses \(S_{U_j}\) and \(V_j\) chooses \(S_{V_j}\) is less than \(X\), then the state \((S_{U_j} \text{ and } S_{V_j})\) is not allowed. - In logical form, this becomes \(\neg (S_{U_j} \land S_{V_j})\), which can be converted to a 2-SAT clause \((\neg S_{U_j} \lor \neg S_{V_j})\).

By solving this decision problem with binary search, we can find the maximum value \(K\).

2. Constructing the Lexicographically Smallest \(S\) (Greedy Method)

Once the maximum value \(K\) is found, we need to make the string \(S\) lexicographically smallest while satisfying \(f(S) \geq K\). The standard approach for finding the lexicographically smallest solution is a greedy method: “assign the smallest possible value (‘0’) to each position from left to right.”

  1. Determine the choice for presenter \(i\) in order from \(i = 1\) to \(N\).
  2. First assume \(S_i = 0\), and check whether there exists an assignment of \(S_{i+1}, \dots, S_N\) satisfying the following conditions:
    • It is consistent with the already determined values \(S_1, \dots, S_i\).
    • The time difference for all pairs is at least \(K\).
  3. The check again uses 2-SAT. To fix \(S_i = 0\), add the clause \((S_i=0 \lor S_i=0)\) to the 2-SAT instance.
  4. If \(S_i = 0\) is feasible, fix it; otherwise, fix \(S_i = 1\).

Algorithm

  1. Binary Search:
    • Binary search for \(X\) in the range \([0, 2 \times 10^9]\).
    • In check(X), examine the 4 patterns of time differences for each pair \((U_j, V_j)\), and add combinations where the difference is less than \(X\) as 2-SAT constraints.
    • Use Strongly Connected Component decomposition (SCC) to determine whether the 2-SAT instance is satisfiable.
  2. Determining the Lexicographically Smallest Solution:
    • Fix the obtained maximum value \(K\).
    • For \(i=1 \dots N\), sequentially check whether “fixing \(S_i=0\) still keeps the 2-SAT satisfiable,” and construct the string \(S\).

Complexity

  • Binary search part: \(O(\log(\max A_i) \cdot (N + M))\)
  • Lexicographically smallest part: \(O(N \cdot (N + M))\)
  • Overall: \(O((\log(\max A_i) + N) \cdot (N + M))\)

Given the constraints \(N \leq 2000, M \leq 5000\), and \(N \times M \leq 10^6\), the total number of operations is on the order of several million, which comfortably fits within the time limit.

Implementation Notes

  • 2-SAT Construction: For variable \(i\), assign node \(2i\) to “choice 1 (True)” and node \(2i+1\) to “choice 0 (False)”.

  • Adding Constraints: The constraint “forbid \(x\) and \(y\) from being simultaneously true” is \(\neg (x \land y) \equiv (\neg x \lor \neg y)\), which translates to the implications \((\text{if } x \text{ then } \neg y)\) and \((\text{if } y \text{ then } \neg x)\), added as edges in the implication graph.

  • SCC: Determining 2-SAT satisfiability requires strongly connected component decomposition using Kosaraju’s algorithm or Tarjan’s algorithm. The instance is satisfiable if and only if no variable has its True node and False node in the same strongly connected component.

  • Case \(M=0\): Following the problem statement’s definition, when \(M=0\), set \(K=0\) and be careful to output 00...0 as the lexicographically smallest solution.

    Source Code

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

// The maximum number of nodes in the implication graph is 2 * N, where N <= 2000.
const int MAXNV = 4005;

// Global adjacency and reverse adjacency lists for Kosaraju's algorithm.
vector<int> adj[MAXNV];
vector<int> rev_adj[MAXNV];
int component[MAXNV];
bool visited[MAXNV];
int order[MAXNV];
int order_ptr;

// First DFS pass of Kosaraju's algorithm to determine the finishing order.
void dfs1(int u) {
    visited[u] = true;
    for (int v : adj[u]) {
        if (!visited[v]) dfs1(v);
    }
    order[order_ptr++] = u;
}

// Second DFS pass of Kosaraju's algorithm on the reverse graph to find SCCs.
void dfs2(int u, int c) {
    component[u] = c;
    for (int v : rev_adj[u]) {
        if (component[v] == -1) dfs2(v, c);
    }
}

// Solves the 2-SAT problem using Kosaraju's algorithm for SCC.
// nv is the total number of nodes (2 * N), and n is the number of variables (N).
bool is_satisfiable(int nv, int n) {
    for (int i = 0; i < nv; ++i) {
        visited[i] = false;
        component[i] = -1;
    }
    order_ptr = 0;
    for (int i = 0; i < nv; ++i) {
        if (!visited[i]) dfs1(i);
    }
    int c = 0;
    for (int i = nv - 1; i >= 0; --i) {
        if (component[order[i]] == -1) dfs2(order[i], c++);
    }
    for (int i = 0; i < n; ++i) {
        // A 2-SAT instance is satisfiable if no variable x_i and its negation !x_i are in the same SCC.
        if (component[2 * i] == component[2 * i + 1]) return false;
    }
    return true;
}

// Adds a clause (x_i == f OR x_j == g) to the 2-SAT instance.
// f and g are boolean values representing the required state of x_i and x_j.
void add_clause(int i, bool f, int j, bool g) {
    // x_i is represented by node 2*i (true) and 2*i + 1 (false).
    int node_i_true = 2 * i;
    int node_i_false = 2 * i + 1;
    int node_j_true = 2 * j;
    int node_j_false = 2 * j + 1;

    int u = f ? node_i_true : node_i_false;
    int not_u = f ? node_i_false : node_i_true;
    int v = g ? node_j_true : node_j_false;
    int not_v = g ? node_j_false : node_j_true;

    // The clause (u OR v) is equivalent to (NOT u -> v) and (NOT v -> u).
    adj[not_u].push_back(v);
    adj[not_v].push_back(u);
    rev_adj[v].push_back(not_u);
    rev_adj[u].push_back(not_v);
}

// Builds the 2-SAT graph for a specific threshold X and checks if it's satisfiable.
bool check(long long X, int N, int M, const vector<long long>& A, const vector<long long>& D, 
           const vector<pair<int, int>>& pairs, const vector<pair<int, bool>>& extra_clauses) {
    int nv = 2 * N;
    for (int i = 0; i < nv; ++i) {
        adj[i].clear();
        rev_adj[i].clear();
    }
    // For each given pair of presenters, forbid start time combinations that result in a difference < X.
    for (auto& p : pairs) {
        int u = p.first, v = p.second;
        long long tU0 = A[u], tU1 = A[u] + D[u];
        long long tV0 = A[v], tV1 = A[v] + D[v];
        
        // If |tU - tV| < X, then the combination (S_u, S_v) is forbidden.
        // This is added as a 2-SAT clause: (NOT S_u OR NOT S_v).
        if ((tU0 > tV0 ? tU0 - tV0 : tV0 - tU0) < X) add_clause(u, true, v, true);
        if ((tU0 > tV1 ? tU0 - tV1 : tV1 - tU0) < X) add_clause(u, true, v, false);
        if ((tU1 > tV0 ? tU1 - tV0 : tV0 - tU1) < X) add_clause(u, false, v, true);
        if ((tU1 > tV1 ? tU1 - tV1 : tV1 - tU1) < X) add_clause(u, false, v, false);
    }
    // Add constraints for already fixed presenter choices in the lexicographical search.
    for (auto& ec : extra_clauses) {
        add_clause(ec.first, ec.second, ec.first, ec.second);
    }
    return is_satisfiable(nv, N);
}

int main() {
    // Faster I/O for competitive programming.
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    int N, M;
    if (!(cin >> N >> M)) return 0;

    vector<long long> A(N), D(N);
    for (int i = 0; i < N; ++i) cin >> A[i] >> D[i];

    vector<pair<int, int>> pairs(M);
    for (int i = 0; i < M; ++i) {
        cin >> pairs[i].first >> pairs[i].second;
        pairs[i].first--; // Convert to 0-indexed.
        pairs[i].second--;
    }

    // Binary search for the maximum possible minimum difference K.
    long long K = 0;
    if (M > 0) {
        long long low = 0, high = 2000000000LL;
        while (low <= high) {
            long long mid = low + (high - low) / 2;
            if (check(mid, N, M, A, D, pairs, {})) {
                K = mid;
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
    }

    // Output the maximum K.
    cout << K << "\n";

    // Greedy search to find the lexicographically smallest string S that achieves K.
    string S = "";
    vector<pair<int, bool>> extra_clauses;
    for (int i = 0; i < N; ++i) {
        // Try setting S[i] = '0' (choice A_i).
        extra_clauses.push_back({i, false});
        if (check(K, N, M, A, D, pairs, extra_clauses)) {
            S += '0';
        } else {
            // If '0' is not possible, set S[i] = '1' (choice A_i + D_i).
            extra_clauses.pop_back();
            extra_clauses.push_back({i, true});
            S += '1';
        }
    }
    cout << S << "\n";

    return 0;
}

This editorial was generated by gemini-3-flash-thinking.

投稿日時:
最終更新: