Submission #1306073

#TimeUsernameProblemLanguageResultExecution timeMemory
1306073orzorzorzTeams (IOI15_teams)C++20
0 / 100
436 ms141788 KiB
/** * IOI 2015 - Teams * Solution using Persistent Segment Tree and Monotonic Stack * Time Complexity: O((N + sum(M)) * log N) * Space Complexity: O(N log N) */ #include <vector> #include <algorithm> #include <iostream> using namespace std; const int MAXN = 500005; const int INF = 1e9 + 7; struct Node { int sum; int l, r; } tree[MAXN * 25]; int root[MAXN]; int node_cnt = 0; int N_val; // Build an empty tree int build(int l, int r) { int u = ++node_cnt; tree[u].sum = 0; if (l == r) return u; int mid = (l + r) / 2; tree[u].l = build(l, mid); tree[u].r = build(mid + 1, r); return u; } // Update the tree persistently int update(int prev, int l, int r, int pos) { int u = ++node_cnt; tree[u] = tree[prev]; tree[u].sum++; if (l == r) return u; int mid = (l + r) / 2; if (pos <= mid) tree[u].l = update(tree[prev].l, l, mid, pos); else tree[u].r = update(tree[prev].r, mid + 1, r, pos); return u; } // Query sum in range [ql, qr] int query(int u, int l, int r, int ql, int qr) { if (ql > r || qr < l) return 0; if (ql <= l && r <= qr) return tree[u].sum; int mid = (l + r) / 2; return query(tree[u].l, l, mid, ql, qr) + query(tree[u].r, mid + 1, r, ql, qr); } // Function to find the smallest x such that: // Count(A <= x, y_low <= B <= y_high) >= val // This binary searches over the versions of the persistent tree (roots). int find_split(int val, int y_low, int y_high) { int l = 1, r = N_val; int ans = N_val + 1; while (l <= r) { int mid = (l + r) / 2; if (query(root[mid], 1, N_val, y_low, y_high) >= val) { ans = mid; r = mid - 1; } else { l = mid + 1; } } return ans; } void init(int N, int A[], int B[]) { N_val = N; // Prepare students sorted by A vector<pair<int, int>> students(N); for (int i = 0; i < N; i++) { students[i] = {A[i], B[i]}; } sort(students.begin(), students.end()); // Build initial empty tree root[0] = build(1, N); // Build persistent versions based on A // root[x] contains all students with A[i] <= x int ptr = 0; for (int x = 1; x <= N; x++) { root[x] = root[x - 1]; while (ptr < N && students[ptr].first == x) { root[x] = update(root[x], 1, N, students[ptr].second); ptr++; } } } int can(int M, int K[]) { // Sort the current query requirements vector<int> k_sorted(M); for (int i = 0; i < M; i++) k_sorted[i] = K[i]; sort(k_sorted.begin(), k_sorted.end()); // Monotonic Stack // Stores pair {index_in_k_sorted, split_value_x} // We use a custom struct or pair struct Element { int idx; int split_val; }; vector<Element> st; // Dummy element for the base case (index -1) // S[-1] is effectively 0. K[-1+1] = K[0]. st.push_back({-1, -INF}); // Used to track sums of team sizes // We compute prefix sums on the fly or just use current logic // Using simple accumulation might overflow int if M is large, // but Constraints say sum of M is small. However, K[i] can be N. // Sum can exceed 2^31. Use long long. // But inside the logic, we check differences. // Let's store prefix sums array. vector<long long> S(M + 1, 0); // S[i] = sum(K[0]...K[i]). // We map index -1 to a virtual S[-1] = 0. // To simplify, let's just use S[i] as sum of K[0]...K[i]. long long current_S = 0; for (int i = 0; i < M; i++) { current_S += k_sorted[i]; S[i] = current_S; // 1. Check validity using the best constraint from the stack // We need the stack element whose split_val <= k_sorted[i] // Since split_val is monotonic in the stack, we can remove elements from the bottom // if we used a deque, but vector is fine with an index pointer, // or just binary search / verify the top isn't the only way. // Actually, since we only add elements with HIGHER split_val, // and we query with increasing k_sorted[i], we effectively "move right". // BUT, we are building the stack as we go. The `i` is the query point. // The stack contains `j < i`. // We just need to remove elements from the BACK if they are dominated by new `i`. // But for querying the validity of `i` itself, we look at the stack constructed so far. // The active constraint is the one with highest split_val <= k_sorted[i]. // Since we cleaned the stack in previous steps, and k is increasing, // we might need to search. However, usually the stack top or near top is relevant? // No, the stack stores history. We need binary search on the stack. // Wait, the standard "Monotonic Stack" for this problem implies we pop from the back // when adding `i`, but for checking `i`, we use the stack. // Let's implement binary search on the stack to find the active segment. int l = 0, r = st.size() - 1; int best_idx = -1; while (l <= r) { int mid = (l + r) / 2; if (st[mid].split_val <= k_sorted[i]) { best_idx = mid; l = mid + 1; } else { r = mid - 1; } } int j = st[best_idx].idx; long long S_j = (j == -1) ? 0 : S[j]; // The minimal B requirement for group j+1 ... i is K[j+1] // Range of B: [K[j+1], N] // Count available students with A <= K[i] and B >= K[j+1] int count = query(root[k_sorted[i]], 1, N_val, k_sorted[j + 1], N_val); if (count < current_S - S_j) { return 0; } // 2. Add current i to stack // We need to determine where `i` dominates the current stack top. while (true) { int top_idx = st.back().idx; // Compare i with top_idx // We want to find smallest x such that: // Count(A <= x, B in [K[top_idx+1], K[i+1] - 1]) >= S[i] - S[top_idx] // Range B: int y_low = k_sorted[top_idx + 1]; int y_high = k_sorted[i + 1] - 1; // Note: if i was the last element, we don't add it to stack? // The problem loops 0..M-1. We need to add i to stack if i < M-1. // Because j serves as the "start of a range" for FUTURE queries. // If i is the last element, it won't be a 'j' for anyone. if (i == M - 1) break; long long diff = S[i] - ((top_idx == -1) ? 0 : S[top_idx]); // If the range is invalid (y_low > y_high), it means K[top+1] > K[i+1]-1. // Since K is sorted, K[top+1] <= K[i+1]. // If K[top+1] == K[i+1], range is empty, count is 0. // If diff > 0, split becomes INF. int split = find_split((int)diff, y_low, y_high); if (split <= st.back().split_val) { st.pop_back(); if (st.empty()) { // Should not happen given dummy -1, but for safety st.push_back({i, split}); break; } } else { st.push_back({i, split}); break; } } } return 1; } // Helper for the local testing environment or grader // This part is conditional based on whether main() is provided by grader #ifdef LOCAL_TEST int main() { int N; if (!(cin >> N)) return 0; vector<int> A(N), B(N); for(int i=0; i<N; i++) cin >> A[i] >> B[i]; init(N, A.data(), B.data()); int Q; cin >> Q; while(Q--) { int M; cin >> M; vector<int> K(M); for(int i=0; i<M; i++) cin >> K[i]; cout << can(M, K.data()) << endl; } return 0; } #endif
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...