Submission #991230

#TimeUsernameProblemLanguageResultExecution timeMemory
991230model_codeFire (BOI24_fire)C++17
100 / 100
214 ms41496 KiB
/* TASK: The Holy Fire (fire) * Task proposal by: Sandra Schumann and Ahto Truu (EST) * Solution code by: Aldas Lenkšas (LTU SC) * Task also prepared by: Daumilas Ardickas (LTU SC) * * The task is to select the minimum number of intervals, such that the full circle * is covered by them. Circle length is M. * * Useful observation is that we can safely remove all the intervals that are fully covered * by some other interval. They are not needed since we can always take the longer interval * instead (that would cover the same interval, and possibly some more points). Note that if * we have two same intervals (same beginning, same end), then we should keep one of them. * * This is not needed, but for simplicity, this solution changes intervals on the circle to the * intervals on the line (it makes some implementation parts easier). If the interval [s[i], e[i]] * is such that e[i] < s[i], then on the line it is the interval [s[i], e[i] + M]. Also, we * duplicate each of the intervals: each original interval [s[i], e[i]] gets a duplicate interval * [s[i] + M, e[i] + M]. To get the original answer (minimum number of intervals, such that the * full circle is covered), now it is enough to find minimum number of intervals on this line, * such that some continuous segment of length M is covered. * * The further solution assumes we are dealing with intervals on the line and no interval is * fully covered by some other interval. * * Observe that for each interval i that we could take, there is some next best interval that * we would take. We can denote it as next[i]. * * O(n^2) solution would start at some interval a, and then take b=next[a], then c=next[b], and so * on, until the segment of length M is taken. Then we can try another starting interval a. * We check all intervals as starting ones, and out of that we choose what we found to be the * minimum number of intervals to cover the segment of length M. * * If by doing so we couldn't continuously cover a segment of length M, the answer is -1. * * To speed up this solution, we can add binary lifting to this. Now for each starting position, * we do (same as in binary search) checking "where would be the end, if we start from interval a, * and take K intervals". We can find the minimum such K. This yields O(n log n) solution. * * Note: here we did not explain the binary lifting in great detail. You can check a better explanation * here: https://cp-algorithms.com/graph/lca_binary_lifting.html */ #include <bits/stdc++.h> using namespace std; const int MAXN = 4e5; // twice the contraints (since we duplicate intervals in modifyIntervals()) const int MAXK = 21; // ~log(MAXN), for binary lifting long long n, m; long long s[MAXN], e[MAXN]; int nextBest[MAXN]; // precalculated next best interval int lift[MAXN][MAXK]; // precalculated binary lifting. lift[i][k] is taking 2^k intervals, starting from i-th one void readInput() { cin >> n >> m; for (int i = 0; i < n; i++) { cin >> s[i] >> e[i]; } } void removeUnnecessaryIntervals() { // interval is unnecessary if it is covered fully by other intervals int cnt = 0; // sort the intervals vector<pair<long long, long long>> intervals; for (int i = 0; i < n; i++) intervals.push_back({s[i], -e[i]}); // this will help sort by increasing s[i], // and then by decreasing e[i] sort(intervals.begin(), intervals.end()); long long furthestEnd = -1; for (auto interval : intervals) { long long start = interval.first; long long end = -interval.second; if (end <= furthestEnd) { continue; // this interval is unnecessary } furthestEnd = end; s[cnt] = start; e[cnt] = end; cnt++; } n = cnt; // update interval count } void modifyIntervals() { // to make implementation a bit easier // deal with the cases of s > e for (int i = 0; i < n; i++) { if (s[i] > e[i]) e[i] += m; } // duplicate all intervals: (s, e) -> (s+m, e+m) for (int i = 0; i < n; i++) { s[i+n] = s[i] + m; e[i+n] = e[i] + m; } n *= 2; // since we duplicated removeUnnecessaryIntervals(); // remove covered fully by other intervals } void precalcNexts() { // sort intervals by their starts vector<pair<long long, int>> intervals; // vector of pairs {start, id} for (int i = 0; i < n; i++) intervals.push_back({s[i], i}); sort(intervals.begin(), intervals.end()); // calculate nexts with two pointers technique int i = 0; for (auto interval : intervals) { int id = interval.second; while (i < n) { int intervalId = intervals[i].second; if (s[intervalId] > e[id]) // not overlapping break; i++; } nextBest[id] = intervals[i - 1].second; } } void precalcBinaryLifting() { for (int i = 0; i < n; i++) lift[i][0] = nextBest[i]; for (int k = 1; k < MAXK; k++) for (int i = 0; i < n; i++) lift[i][k] = lift[ lift[i][k-1] ][k-1]; } int tryFrom(int startAdmin) { long long startTime = s[startAdmin]; // do binary lifting (binary search) to find the furthest reachable admin // such that it would still not cover full circle int cntJumps = 0; int curAdmin = startAdmin; for (int i = MAXK-1; i >= 0; i--) { int jumpedToId = lift[curAdmin][i]; if (e[jumpedToId] < (startTime + m)) { // does not cover full circle cntJumps += (1<<i); // 2^i curAdmin = jumpedToId; } } // we found the last before we get to full circle cntJumps++; curAdmin = lift[curAdmin][0]; if (e[curAdmin] < (startTime + m)) { return -1; // impossible since we did not cover full circle } return cntJumps + 1; } int main() { readInput(); modifyIntervals(); // see the explanation at the top precalcNexts(); precalcBinaryLifting(); int ans = -1; for (int i = 0; i < n; i++) { int curTaken = tryFrom(i); // update ans if (curTaken == -1) continue; if (ans == -1 || curTaken < ans) ans = curTaken; } cout << ans << endl; 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...
#Verdict Execution timeMemoryGrader output
Fetching results...