Submission #810337

#TimeUsernameProblemLanguageResultExecution timeMemory
810337model_codeCell Automaton (JOI23_cell)C++17
100 / 100
4138 ms698756 KiB
/*
	FULL-SCORE SOLUTION
	- Time Complexity: O((Q + N) log N)
*/

#include <set>
#include <tuple>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

class value {
public:
	long long x; int p1, p2; // value: x + p1 * eps + p2 * eps^2 (eps > 0 is an arbitrary value which is small enough)
	value() : x(0), p1(0), p2(0) {}
	value(long long x_, int p1_, int p2_) : x(x_), p1(p1_), p2(p2_) {}
	bool operator<(const value& v) const { return x != v.x ? x < v.x : (p1 != v.p1 ? p1 < v.p1 : p2 < v.p2); }
	bool operator>(const value& v) const { return x != v.x ? x > v.x : (p1 != v.p1 ? p1 > v.p1 : p2 > v.p2); }
	value operator+() const { return value(+x, +p1, +p2); }
	value operator-() const { return value(-x, -p1, -p2); }
	value& operator+=(const value& v) { x += v.x; p1 += v.p1; p2 += v.p2; return *this; }
	value& operator-=(const value& v) { x -= v.x; p1 -= v.p1; p2 -= v.p2; return *this; }
	value operator+(const value& v) const { return value(*this) += v; }
	value operator-(const value& v) const { return value(*this) -= v; }
};

class nearest_info {
public:
	int idx; value d1, d2;
	nearest_info() : idx(-1), d1(value()), d2(value()) {}
	nearest_info(int idx_, const value& d1_, const value& d2_) : idx(idx_), d1(d1_), d2(d2_) {}
};

class segment {
public:
	long long a, b, l, r; // y = ax + b (l <= x <= r)
	segment() : a(0), b(0), l(0), r(0) {}
	segment(long long a_, long long b_, long long l_, long long r_) : a(a_), b(b_), l(l_), r(r_) {}
};

// FUNCTION FOR SOLVING "CONTINUOUS VERSION" OF THE PROBLEM
// - Given N points (X[0], Y[0]), ..., (X[N-1], Y[N-1]) and Q queries with T[0], T[1], ..., T[Q-1]
// - For each query j, answer the following:
//   - Consider N squares that, for i = 0, 1, ..., N-1, the lower-left point is at (X[i], Y[i]) and the upper-right point is at (X[i]+T[j], Y[i]+T[j]).
//   - Calculate the area of union of these N squares.
vector<long long> area_union(int N, int Q, const vector<long long>& X, const vector<long long>& Y, const vector<long long>& T) {
	// step #1. compute nearest colliding square, for each square's corner
	vector<value> xp(N), yp(N);
	for (int i = 0; i < N; i++) {
		xp[i] = value(X[i], i, 0);
		yp[i] = value(Y[i], 0, i);
	}
	vector<vector<nearest_info> > nearest(8, vector<nearest_info>(N)); // d1: distance, d2: "orthogonal" distance
	for (int i = 0; i < 8; i++) {
		vector<value> xv(N), yv(N);
		for (int j = 0; j < N; j++) {
			switch (i) {
				case 0: xv[j] = +xp[j]; yv[j] = +yp[j]; break;
				case 1: xv[j] = +xp[j]; yv[j] = -yp[j]; break;
				case 2: xv[j] = -yp[j]; yv[j] = +xp[j]; break;
				case 3: xv[j] = -yp[j]; yv[j] = -xp[j]; break;
				case 4: xv[j] = -xp[j]; yv[j] = -yp[j]; break;
				case 5: xv[j] = -xp[j]; yv[j] = +yp[j]; break;
				case 6: xv[j] = +yp[j]; yv[j] = -xp[j]; break;
				case 7: xv[j] = +yp[j]; yv[j] = +xp[j]; break;
			}
		}
		vector<int> perm(N);
		for (int i = 0; i < N; i++) {
			perm[i] = i;
		}
		sort(perm.begin(), perm.end(), [&](int va, int vb) {
			return yv[va] < yv[vb];
		});
		set<value> s;
		for (int j : perm) {
			set<value>::iterator it = s.lower_bound(xv[j] - yv[j]);
			while (it != s.end() && xv[abs(it->p1)] < xv[j]) {
				it = s.erase(it);
			}
			if (it != s.begin()) {
				--it;
				nearest[i][j] = nearest_info(abs(it->p1), xv[j] - xv[abs(it->p1)], yv[j] - yv[abs(it->p1)]);
			}
			s.insert(xv[j] - yv[j]);
		}
	}
	for (int i = 0; i < 8; i += 2) {
		int p = vector<int>({ 7, 2, 1, 4, 3, 6, 5, 0 })[i];
		for (int j = 0; j < N; j++) {
			if (nearest[i][j].idx != -1 && nearest[p][j].idx != -1) {
				if (nearest[i][j].d1 > nearest[p][j].d1) {
					nearest[i][j] = nearest_info();
				}
				else {
					nearest[p][j] = nearest_info();
				}
			}
		}
	}

	// step #2. compute "time lapse" of each square's edge
	vector<vector<vector<nearest_info> > > timelapse(8, vector<vector<nearest_info> >(N)); // d1: time, d2: distance from another corner
	for (int i = 0; i < 8; i++) {
		int p1 = vector<int>({ 7, 2, 1, 4, 3, 6, 5, 0 })[i];
		int p2 = vector<int>({ 4, 5, 6, 7, 0, 1, 2, 3 })[i];
		for (int j = 0; j < N; j++) {
			if (nearest[p1][j].idx != -1) {
				timelapse[i][j].push_back(nearest_info(nearest[p1][j].idx, nearest[p1][j].d1, nearest[p1][j].d1));
			}
			if (nearest[p2][j].idx != -1) {
				timelapse[i][nearest[p2][j].idx].push_back(nearest_info(j, nearest[p2][j].d1, nearest[p2][j].d2));
			}
		}
		for (int j = 0; j < N; j++) {
			sort(timelapse[i][j].begin(), timelapse[i][j].end(), [&](const nearest_info& f1, const nearest_info& f2) {
				return f1.d1 < f2.d1;
			});
		}
	}

	// step #3. find all crashes & rectangular holes
	const long long INF = (3LL << 60);
	vector<vector<value> > eliminate(4, vector<value>(N, value(INF, 0, 0)));
	for (int i = 0; i < 8; i += 2) {
		for (int j = 0; j < N; j++) {
			if (!timelapse[i + 0][j].empty() && !timelapse[i + 1][j].empty()) {
				nearest_info f0 = timelapse[i + 0][j].back();
				nearest_info f1 = timelapse[i + 1][j].back();
				value t = f0.d2 + f1.d2;
				if ((timelapse[(i + 2) % 8][f0.idx].empty() || timelapse[(i + 2) % 8][f0.idx].back().d1 < t) && timelapse[(i + 3) % 8][f0.idx].back().d1 < t) {
					eliminate[((i + 2) % 8) / 2][f0.idx] = min(eliminate[((i + 2) % 8) / 2][f0.idx], t);
				}
				if ((timelapse[(i + 7) % 8][f1.idx].empty() || timelapse[(i + 7) % 8][f1.idx].back().d1 < t) && timelapse[(i + 6) % 8][f1.idx].back().d1 < t) {
					eliminate[((i + 6) % 8) / 2][f1.idx] = min(eliminate[((i + 6) % 8) / 2][f1.idx], t);
				}
				eliminate[i / 2][j] = min(eliminate[i / 2][j], t);
			}
		}
	}

	// step #4. final calculation
	vector<segment> seg;
	for (int i = 0; i < 8; i += 2) {
		for (int j = 0; j < N; j++) {
			seg.push_back(segment(1, 0, 0, eliminate[i / 2][j].x));
			for (int k = 0; k < 2; k++) {
				if (!timelapse[i + k][j].empty()) {
					seg.push_back(segment(-1, timelapse[i + k][j][0].d2.x, timelapse[i + k][j][0].d1.x, eliminate[i / 2][j].x));
					for (int l = 1; l < timelapse[i + k][j].size(); l++) {
						seg.push_back(segment(0, -(timelapse[i + k][j][l - 1].d2 - timelapse[i + k][j][l].d2).x, timelapse[i + k][j][l].d1.x, eliminate[i / 2][j].x));
					}
				}
			}
		}
	}
	vector<long long> tcomp({ 0, INF });
	for (segment s : seg) {
		if ((s.a != 0 || s.b != 0) && s.l != s.r) {
			tcomp.push_back(s.l);
			tcomp.push_back(s.r);
		}
	}
	sort(tcomp.begin(), tcomp.end());
	tcomp.erase(unique(tcomp.begin(), tcomp.end()), tcomp.end());
	vector<long long> polya(tcomp.size()), polyb(tcomp.size()); // y = ax + b
	for (segment s : seg) {
		if ((s.a != 0 || s.b != 0) && s.l != s.r) {
			int pl = lower_bound(tcomp.begin(), tcomp.end(), s.l) - tcomp.begin();
			int pr = lower_bound(tcomp.begin(), tcomp.end(), s.r) - tcomp.begin();
			polya[pl] += s.a;
			polya[pr] -= s.a;
			polyb[pl] += s.b;
			polyb[pr] -= s.b;
		}
	}
	for (int i = 1; i < tcomp.size(); i++) {
		polya[i] += polya[i - 1];
		polyb[i] += polyb[i - 1];
	}
	vector<long long> cum(tcomp.size());
	for (int i = 0; i < int(tcomp.size()) - 1; i++) {
		long long yl = polya[i] * tcomp[i] + polyb[i];
		long long yr = polya[i] * tcomp[i + 1] + polyb[i];
		cum[i + 1] = cum[i] + (yl + yr) * (tcomp[i + 1] - tcomp[i]) / 4;
	}
	vector<long long> answer(Q);
	for (int i = 0; i < Q; i++) {
		int pos = lower_bound(tcomp.begin(), tcomp.end(), T[i] + 1) - tcomp.begin() - 1;
		long long yl = polya[pos] * tcomp[pos] + polyb[pos];
		long long yt = polya[pos] * T[i] + polyb[pos];
		answer[i] = cum[pos] + (yl + yt) * (T[i] - tcomp[pos]) / 4;
	}
	return answer;
}

// FUNCTION FOR SOLVING THE MAIN PROBLEM
vector<long long> solve(int N, int Q, const vector<long long>& X, const vector<long long>& Y, const vector<long long>& T) {
	auto half = [&](long long x) {
		return (x >= 0 ? x / 2 : -((-x + 1) / 2));
	};
	vector<long long> xa, ya, xb, yb;
	for (int i = 0; i < N; i++) {
		if ((X[i] + Y[i]) % 2 == 0) {
			xa.push_back((X[i] + Y[i] + 0) / 2); ya.push_back((X[i] - Y[i] + 0) / 2);
			xa.push_back((X[i] + Y[i] + 0) / 2); ya.push_back((X[i] - Y[i] + 2) / 2);
			xa.push_back((X[i] + Y[i] + 2) / 2); ya.push_back((X[i] - Y[i] + 0) / 2);
			xa.push_back((X[i] + Y[i] + 2) / 2); ya.push_back((X[i] - Y[i] + 2) / 2);
			xb.push_back((X[i] + Y[i] + 2) / 2); yb.push_back((X[i] - Y[i] + 2) / 2);
		}
		else {
			xa.push_back((X[i] + Y[i] + 1) / 2); ya.push_back((X[i] - Y[i] + 1) / 2);
			xb.push_back((X[i] + Y[i] + 1) / 2); yb.push_back((X[i] - Y[i] + 1) / 2);
			xb.push_back((X[i] + Y[i] + 1) / 2); yb.push_back((X[i] - Y[i] + 3) / 2);
			xb.push_back((X[i] + Y[i] + 3) / 2); yb.push_back((X[i] - Y[i] + 1) / 2);
			xb.push_back((X[i] + Y[i] + 3) / 2); yb.push_back((X[i] - Y[i] + 3) / 2);
		}
	}
	vector<long long> T2(Q * 2);
	for (int i = 0; i < Q; i++) {
		T2[i * 2 + 0] = T[i];
		T2[i * 2 + 1] = (T[i] >= 1 ? T[i] - 1 : 0);
	}
	vector<long long> resa = area_union(xa.size(), Q * 2, xa, ya, T2);
	vector<long long> resb = area_union(xb.size(), Q * 2, xb, yb, T2);
	vector<long long> answer(Q, 0);
	for (int i = 0; i < Q; i++) {
		if (T[i] == 0) {
			answer[i] = N;
		}
		else {
			answer[i] += resa[2 * i + 0] - (T[i] >= 2 ? resa[2 * i + 1] : 0);
			answer[i] += resb[2 * i + 0] - (T[i] >= 2 ? resb[2 * i + 1] : 0);
			if (T[i] == 1) {
				answer[i] -= N;
			}
		}
	}
	return answer;
}

int main() {
	cin.tie(0);
	ios_base::sync_with_stdio(false);
	int N, Q;
	cin >> N >> Q;
	vector<long long> X(N), Y(N), T(Q);
	for (int i = 0; i < N; i++) {
		cin >> X[i] >> Y[i];
	}
	for (int i = 0; i < Q; i++) {
		cin >> T[i];
	}
	vector<long long> answer = solve(N, Q, X, Y, T);
	for (int i = 0; i < Q; i++) {
		cout << answer[i] << '\n';
	}
	return 0;
}

Compilation message (stderr)

cell.cpp: In function 'std::vector<long long int> area_union(int, int, const std::vector<long long int>&, const std::vector<long long int>&, const std::vector<long long int>&)':
cell.cpp:151:24: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<nearest_info>::size_type' {aka 'long unsigned int'} [-Wsign-compare]
  151 |      for (int l = 1; l < timelapse[i + k][j].size(); l++) {
      |                      ~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
cell.cpp:178:20: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector<long long int>::size_type' {aka 'long unsigned int'} [-Wsign-compare]
  178 |  for (int i = 1; i < tcomp.size(); i++) {
      |                  ~~^~~~~~~~~~~~~~
cell.cpp: In function 'std::vector<long long int> solve(int, int, const std::vector<long long int>&, const std::vector<long long int>&, const std::vector<long long int>&)':
cell.cpp:200:7: warning: variable 'half' set but not used [-Wunused-but-set-variable]
  200 |  auto half = [&](long long x) {
      |       ^~~~
#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...
#Verdict Execution timeMemoryGrader output
Fetching results...