Submission #1308068

#TimeUsernameProblemLanguageResultExecution timeMemory
1308068orzorzorz팀들 (IOI15_teams)C++20
100 / 100
1834 ms155744 KiB
#include <vector> #include <algorithm> #include <iostream> using namespace std; // --- Persistent Segment Tree --- // Allows querying: count students with B >= Y within a range of A values. const int MAXN = 500005; const int MAX_NODES = MAXN * 25; struct Node { int l, r; int sum; } tree[MAX_NODES]; int roots[MAXN]; // roots[i] stores the segment tree for students with A <= i int node_cnt = 0; int N_global; // Build an empty tree int build(int tl, int tr) { int id = ++node_cnt; tree[id].sum = 0; tree[id].l = 0; tree[id].r = 0; if (tl != tr) { int tm = (tl + tr) / 2; tree[id].l = build(tl, tm); tree[id].r = build(tm + 1, tr); } return id; } // Update: Add a student with upper bound 'pos' (B value) int update(int prev_node, int tl, int tr, int pos) { int id = ++node_cnt; tree[id] = tree[prev_node]; tree[id].sum++; if (tl == tr) return id; int tm = (tl + tr) / 2; if (pos <= tm) tree[id].l = update(tree[prev_node].l, tl, tm, pos); else tree[id].r = update(tree[prev_node].r, tm + 1, tr, pos); return id; } // Query: Count students with B in range [ql, qr] // Since we access roots[R] - roots[L], we effectively filter by A first. int query(int node, int tl, int tr, int ql, int qr) { if (ql > qr) return 0; if (ql == tl && qr == tr) return tree[node].sum; int tm = (tl + tr) / 2; if (qr <= tm) return query(tree[node].l, tl, tm, ql, qr); if (ql > tm) return query(tree[node].r, tm + 1, tr, ql, qr); return query(tree[node].l, tl, tm, ql, tm) + query(tree[node].r, tm + 1, tr, tm + 1, qr); } // Counts students satisfying: min_size_pref > A_lower AND max_size_pref >= B_threshold // This maps to: count students in PST range (A_lower, N] with value >= B_threshold int count_students(int a_lower, int b_threshold) { if (b_threshold > N_global) return 0; // We want students with A in (a_lower, N_global]. // Since roots[i] accumulates 1..i, roots[N] - roots[a_lower] gives range (a_lower, N]. // Note: The problem logic often requires students with A <= K_i. // Let's stick to the DP recurrence: count(K_j < A <= K_i, B >= K_i). int cnt_all = query(roots[N_global], 1, N_global, b_threshold, N_global); // All with B >= K_i int cnt_low = query(roots[a_lower], 1, N_global, b_threshold, N_global); // Those with A <= K_j // This function returns students with A > a_lower AND B >= b_threshold // But wait, the standard reduction usually uses: count(A <= a_upper, B >= b_threshold) // Let's adjust for the DP needs: // We usually compute: Count(A <= K_i, B >= K_i) - Count(A <= K_j, B >= K_i) return 0; // Placeholder, logic moved inside solve for clarity } // Specific helper for the DP transition // Returns count of students with A <= a_limit AND B >= b_limit int get_cnt(int a_limit, int b_limit) { if (b_limit > N_global) return 0; return query(roots[a_limit], 1, N_global, b_limit, N_global); } void init(int N, int A[], int B[]) { N_global = N; node_cnt = 0; vector<vector<int>> by_A(N + 1); for (int i = 0; i < N; i++) { by_A[A[i]].push_back(B[i]); } roots[0] = build(1, N); for (int i = 1; i <= N; i++) { roots[i] = roots[i-1]; for (int b_val : by_A[i]) { roots[i] = update(roots[i], 1, N, b_val); } } } struct StackItem { int idx; // Index j int limit_y; // The K value up to which this j is optimal }; int can(int M, int K[]) { // Sort K to apply Hall's on intervals vector<int> k_sorted(M + 1); k_sorted[0] = 0; for (int i = 0; i < M; i++) k_sorted[i+1] = K[i]; sort(k_sorted.begin() + 1, k_sorted.end()); vector<long long> dp(M + 1); // dp[i] = max slack after satisfying 1..i dp[0] = 0; vector<StackItem> st; st.push_back({0, N_global + 1}); for (int i = 1; i <= M; i++) { int k_curr = k_sorted[i]; // 1. Maintain Convex Hull (Find best j) // We pop if the current requested size k_curr exceeds the validity of the top strategy while (st.size() > 1 && st.back().limit_y <= k_curr) { st.pop_back(); } int j = st.back().idx; // 2. DP Recurrence // Slack[i] = Slack[j] + (Supply between j and i) - Demand[i] // Supply = Students with K[j] < A <= K[i] AND B >= K[i] // Calculated as: Count(A <= K[i], B >= K[i]) - Count(A <= K[j], B >= K[i]) int count_upto_i = get_cnt(k_curr, k_curr); int count_upto_j = get_cnt(k_sorted[j], k_curr); dp[i] = dp[j] + (count_upto_i - count_upto_j) - k_curr; if (dp[i] < 0) return 0; // Hall's condition violated // 3. Update Convex Hull (Add i) // We need to find the split point 'ans' (a value of team size Y) // where strategy 'i' becomes worse than 'st.back()'. // Equation: dp[i] + Count(A<=Y, B>=Y) - Count(A<=K[i], B>=Y) >= dp[old] + Count(A<=Y, B>=Y) - Count(A<=K[old], B>=Y) // Simplifies to: dp[i] - Count(A<=K[i], B>=Y) >= dp[old] - Count(A<=K[old], B>=Y) // -> Count(A<=K[i], B>=Y) - Count(A<=K[old], B>=Y) <= dp[i] - dp[old] // LHS is monotonically non-increasing as Y increases (fewer students have B >= large Y) while (true) { int old_idx = st.back().idx; long long diff_dp = dp[i] - dp[old_idx]; // Binary Search for the split point int low = k_curr + 1; int high = N_global + 1; int ans = N_global + 1; while (low <= high) { int mid = (low + high) / 2; // Cost function difference int val = get_cnt(k_curr, mid) - get_cnt(k_sorted[old_idx], mid); if (val <= diff_dp) { ans = mid; high = mid - 1; } else { low = mid + 1; } } if (ans >= st.back().limit_y) { st.pop_back(); if (st.empty()) { st.push_back({i, N_global + 1}); break; } } else { st.push_back({i, ans}); break; } } } return 1; }
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...