Submission #1235131

#TimeUsernameProblemLanguageResultExecution timeMemory
1235131faricaFire (BOI24_fire)C++20
100 / 100
245 ms41244 KiB

#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...