Official

I - タスクスケジュールと優先度フィルタ / Task Schedule and Priority Filter Editorial by admin

Claude 4.6 Opus (Thinking)

Overview

When creating a permutation (schedule) of \(N\) tasks, the problem asks to find the minimum filter strength \(X\) that can ignore priority-based ordering constraints, recalculated each time a new request arrives. The core challenge lies in determining “whether the constraints remaining for a chosen \(X\) form a DAG (Directed Acyclic Graph).”

Analysis

Constraints to Satisfy and DAGs

The ordering constraint of request \(j\), “\(P_{j,1} \to P_{j,2} \to \cdots \to P_{j,K_j}\)”, can be viewed as a collection of directed edges \(P_{j,i} \to P_{j,i+1}\) between consecutive pairs of tasks.

When we collect edges from all active constraints (those with \(W_j > X\)) to build a graph, a valid schedule exists ⟺ the graph is a DAG (i.e., a topological order exists). This is because if the graph contains a cycle, no ordering can satisfy all constraints.

Candidate Values for \(X\)

As \(X\) increases, more requests are ignored and fewer edges remain. The set of constraints only changes when \(X\) crosses some value \(W_j\), so the candidates for \(X\) are limited to at most \(t+1\) values: \(\{0, W_1, W_2, \ldots, W_t\}\).

Binary Search

When the candidate values are sorted, since larger \(X\) means looser constraints, the DAG check result is monotonic (always succeeds above a certain threshold). Therefore, the minimum \(X\) can be efficiently found using binary search.

Algorithm

For each \(t = 1, 2, \ldots, M\):

  1. Collect the priorities \(W_1, \ldots, W_t\) from requests \(1, \ldots, t\), remove duplicates, and sort them.
  2. First, check if all constraints form a DAG with \(X = 0\). If so, \(X_t = 0\).
  3. Otherwise, perform binary search over the candidate values. For each candidate \(X\):
    • Collect edges from requests \(j\) where \(W_j > X\) and build the graph
    • Use Kahn’s algorithm (BFS-based topological sort) for DAG detection
    • If the number of processed nodes equals \(N\), the graph is a DAG (success)
  4. Find the minimum successful \(X\) via binary search.

Finally, output a topological order for the constraints filtered by \(X_M\) (tasks not involved in any constraint are also included in the order to obtain a permutation of \(N\) elements).

Concrete example of DAG detection (\(N=3\), constraints: \(1 \to 2\), \(2 \to 1\)):

  • Edges \(1 \to 2\) and \(2 \to 1\) form a cycle → not a DAG → this \(X\) is infeasible

Complexity

  • At each time point \(t\), binary search runs \(O(\log M)\) times
  • Each check requires \(O(N + \sum K_j)\) for graph construction + topological sort
  • We process \(M\) time points in total

\[\text{Time complexity: } O\!\left(M \cdot \log M \cdot \left(N + \sum_{j=1}^{M} K_j\right)\right)\]

Substituting the upper bounds of the constraints gives approximately \(O(1000 \times 10 \times 5000) = O(5 \times 10^7)\).

  • Space complexity: \(O\!\left(N + \sum K_j\right)\) (for storing the graph)

Implementation Notes

  • Handling multiple edges: Different requests may produce the same edge \(u \to v\), but Kahn’s algorithm manages in-degrees through increments and decrements, so it works correctly even with multiple edges.

  • Outputting the topological order: The getOrder function computes a topological order via BFS. Since nodes with in-degree \(0\) are added to the queue first, tasks not appearing in any constraint are naturally included in the permutation.

  • Special handling for \(X = 0\): Before binary search, \(X = 0\) is checked first to correctly handle the case where all constraints can be satisfied.

    Source Code

#include <bits/stdc++.h>
using namespace std;

int main(){
    int N, M;
    scanf("%d%d", &N, &M);
    
    vector<int> W(M);
    vector<vector<int>> P(M);
    
    for(int j = 0; j < M; j++){
        int K;
        scanf("%d%d", &W[j], &K);
        P[j].resize(K);
        for(int i = 0; i < K; i++){
            scanf("%d", &P[j][i]);
            P[j][i]--;
        }
    }
    
    auto check = [&](int t, int X) -> bool {
        vector<vector<int>> adj(N);
        vector<int> indeg(N, 0);
        for(int j = 0; j < t; j++){
            if(W[j] > X){
                for(int i = 0; i+1 < (int)P[j].size(); i++){
                    adj[P[j][i]].push_back(P[j][i+1]);
                    indeg[P[j][i+1]]++;
                }
            }
        }
        queue<int> q;
        for(int i = 0; i < N; i++) if(indeg[i] == 0) q.push(i);
        int cnt = 0;
        while(!q.empty()){
            int u = q.front(); q.pop();
            cnt++;
            for(int v : adj[u]){
                if(--indeg[v] == 0) q.push(v);
            }
        }
        return cnt == N;
    };
    
    auto getOrder = [&](int t, int X) -> vector<int> {
        vector<vector<int>> adj(N);
        vector<int> indeg(N, 0);
        for(int j = 0; j < t; j++){
            if(W[j] > X){
                for(int i = 0; i+1 < (int)P[j].size(); i++){
                    adj[P[j][i]].push_back(P[j][i+1]);
                    indeg[P[j][i+1]]++;
                }
            }
        }
        queue<int> q;
        for(int i = 0; i < N; i++) if(indeg[i] == 0) q.push(i);
        vector<int> order;
        while(!q.empty()){
            int u = q.front(); q.pop();
            order.push_back(u);
            for(int v : adj[u]){
                if(--indeg[v] == 0) q.push(v);
            }
        }
        return order;
    };
    
    vector<int> answers(M);
    
    for(int t = 1; t <= M; t++){
        vector<int> ws;
        for(int j = 0; j < t; j++) ws.push_back(W[j]);
        sort(ws.begin(), ws.end());
        ws.erase(unique(ws.begin(), ws.end()), ws.end());
        
        if(check(t, 0)){
            answers[t-1] = 0;
        } else {
            int lo = 0, hi = (int)ws.size() - 1;
            while(lo < hi){
                int mid = (lo + hi) / 2;
                if(check(t, ws[mid])) hi = mid;
                else lo = mid + 1;
            }
            answers[t-1] = ws[lo];
        }
    }
    
    for(int t = 0; t < M; t++){
        printf("%d%c", answers[t], t+1<M?' ':'\n');
    }
    
    vector<int> order = getOrder(M, answers[M-1]);
    for(int i = 0; i < N; i++){
        printf("%d%c", order[i]+1, i+1<N?' ':'\n');
    }
    
    return 0;
}

This editorial was generated by claude4.6opus-thinking.

posted:
last update: