Submission #1302161

#TimeUsernameProblemLanguageResultExecution timeMemory
1302161itachi_godRope (JOI17_rope)C++20
0 / 100
0 ms332 KiB
/** * JOI 2016/2017 Final Round - Task 5: Rope * * Solution Logic: * 1. The problem reduces to finding a pivot k such that the cost is minimized. * 2. We solve for the "Left Alignment" case and then reverse the rope for the "Right Alignment" case. * 3. We use a threshold to distinguish between "Heavy" and "Light" colors. * - Heavy Colors: Compute exact cost for all k using prefix sums. O(N log N) per color. * - Light Colors: Identify ranges of k where the count increases using the number theory * property of the folding. Use a sweep-line with RMQ to find the min cost. */ #include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; const int INF = 1e9; int N, M; vector<int> C; // Colors vector<int> final_ans; // Sparse Table for O(1) Range Minimum Query struct SparseTable { vector<vector<int>> st; vector<int> log_table; int n; void build(const vector<int>& arr) { n = arr.size(); log_table.resize(n + 1); log_table[1] = 0; for (int i = 2; i <= n; i++) log_table[i] = log_table[i / 2] + 1; int K = log_table[n]; st.assign(n, vector<int>(K + 1)); for (int i = 0; i < n; i++) st[i][0] = arr[i]; for (int j = 1; j <= K; j++) for (int i = 0; i + (1 << j) <= n; i++) st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } int query(int L, int R) { if (L > R) return INF; int j = log_table[R - L + 1]; return min(st[L][j], st[R - (1 << j) + 1][j]); } }; void solve_pass(const vector<int>& current_colors) { // 1. Calculate Total Size of Left Pile for each k [1, N-1] // The Left Pile for pivot k contains indices in [1, k], [2k+1, 3k], [4k+1, 5k], ... vector<int> total_size(N, 0); // 0-based index k corresponds to pivot k for (int k = 1; k < N; ++k) { int count = 0; for (int m = 0; ; m += 2) { int l = m * k + 1; int r = (m + 1) * k; if (l > N) break; r = min(r, N); count += (r - l + 1); } total_size[k] = count; } // Build RMQ on total_size to quickly query min(Size) in ranges SparseTable rmq; rmq.build(total_size); // Group positions by color vector<vector<int>> positions(M + 1); for (int i = 0; i < N; ++i) { positions[current_colors[i]].push_back(i + 1); // Store 1-based indices } // Threshold tuning: B ~ sqrt(N) * log(N) is usually optimal or just sqrt(N). // With N=1,000,000, sqrt(N)=1000. // Heavy colors approach takes O(N log N). Light takes O(Freq * sqrt(N)). // A threshold of ~3000 balances the two well. const int THRESHOLD = 3000; for (int c = 1; c <= M; ++c) { if (positions[c].empty()) continue; if (positions[c].size() > THRESHOLD) { // --- HEAVY COLOR STRATEGY --- // Iterate all k. Use prefix sums to count occurrences efficiently. vector<int> pref(N + 1, 0); for (int p : positions[c]) pref[p] = 1; for (int i = 1; i <= N; ++i) pref[i] += pref[i - 1]; for (int k = 1; k < N; ++k) { int cnt = 0; // Sum segments [1, k], [2k+1, 3k], ... for (int m = 0; ; m += 2) { int l = m * k + 1; int r = (m + 1) * k; if (l > N) break; r = min(r, N); cnt += (pref[r] - pref[l - 1]); } final_ans[c] = min(final_ans[c], total_size[k] - cnt); } } else { // --- LIGHT COLOR STRATEGY --- // A position p contributes to pivot k if floor((p-1)/k) is Even. // This creates ranges of k. We collect these ranges and sweep. vector<pair<int, int>> events; events.reserve(positions[c].size() * 100); // Heuristic reservation for (int p : positions[c]) { int X = p - 1; // Case q=0: k > X. Range [X+1, N-1] if (X + 1 < N) { events.push_back({X + 1, 1}); events.push_back({N, -1}); } // Iterate even q >= 2 // Valid k range for quotient q is roughly (X/(q+1), X/q] for (int q = 2; ; q += 2) { int R = X / q; if (R == 0) break; // k must be >= 1 int L = X / (q + 1) + 1; // Intersect with valid pivots [1, N-1] int r_bound = min(N - 1, R); int l_bound = max(1, L); if (l_bound <= r_bound) { events.push_back({l_bound, 1}); events.push_back({r_bound + 1, -1}); } if (L > R) continue; // Should not happen with logic but safe check } } if (events.empty()) { final_ans[c] = min(final_ans[c], rmq.query(1, N - 1)); continue; } sort(events.begin(), events.end()); int current_gain = 0; int prev_k = 1; // Sweep line for (size_t i = 0; i < events.size(); ) { int curr_k = events[i].first; // Query the minimum Size for the range where gain was constant if (curr_k > prev_k) { int min_s = rmq.query(prev_k, min(curr_k - 1, N - 1)); if (min_s != INF) { final_ans[c] = min(final_ans[c], min_s - current_gain); } } // Update gain while (i < events.size() && events[i].first == curr_k) { current_gain += events[i].second; i++; } prev_k = curr_k; } // Handle tail [Last Event, N-1] if (prev_k < N) { int min_s = rmq.query(prev_k, N - 1); if (min_s != INF) { final_ans[c] = min(final_ans[c], min_s - current_gain); } } } } } int main() { // Optimize I/O operations ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> N >> M; C.resize(N); for (int i = 0; i < N; ++i) cin >> C[i]; final_ans.assign(M + 1, INF); // Pass 1: Consider "Left Aligned" folding patterns solve_pass(C); // Pass 2: Consider "Right Aligned" folding patterns (equivalent to Left Aligned on reversed rope) vector<int> C_rev = C; reverse(C_rev.begin(), C_rev.end()); solve_pass(C_rev); for (int c = 1; c <= M; ++c) { cout << final_ans[c] << "\n"; } 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...