Submission #301170

#TimeUsernameProblemLanguageResultExecution timeMemory
301170ecnerwalaComparing Plants (IOI20_plants)C++17
100 / 100
410 ms18580 KiB
#include "plants.h"

#include <bits/stdc++.h>

namespace {

class plants_solver {
	struct index_data {
		int l_to_r;
		int l_to_r_2;
		int r_to_l;
		int r_to_l_2;
	};
	std::vector<index_data> v;
public:
	plants_solver() { }
	plants_solver(int N, int K, std::vector<int> R) : v(N) {
		using std::min;
		using std::max;

		assert(N == int(R.size()));
		assert(1 <= K && K <= N);
		for (int& r : R) assert(0 <= r && r < K);

		struct seg_node {
			int val;
			int ind;
			int lazy;
			int last_taken;
		};
		std::vector<seg_node> seg(2*N);
		auto update_node = [&](int a) {
			assert(a < N);
			if (seg[2*a].val < seg[2*a+1].val) {
				seg[a].val = seg[2*a].val;
				seg[a].ind = seg[2*a].ind;
			} else {
				seg[a].val = seg[2*a+1].val;
				seg[a].ind = seg[2*a+1].ind;
			}
			seg[a].val += seg[a].lazy;
		};

		for (int i = 0; i < N; i++) {
			seg[N+i] = seg_node{R[i], i, 0, -1};
		}
		for (int a = N-1; a; a--) {
			update_node(a);
			seg[a].last_taken = -1;
		}

		auto downdate_all = [&](int n) {
			n >>= __builtin_ctz(n); n >>= 1;
			if (!n) return;
			for (int l = 31 - __builtin_clz(n); l >= 0; l--) {
				int a = n >> l;
				assert(a < N);
				seg[2*a].val += seg[a].lazy;
				seg[2*a].lazy += seg[a].lazy;
				seg[2*a+1].val += seg[a].lazy;
				seg[2*a+1].lazy += seg[a].lazy;
				seg[a].lazy = 0;
			}
		};

		std::vector<int> toposort(N); // We'll use the tail of toposort as a stack.
		std::vector<int> prev_bigger(N);
		std::vector<int> next_bigger(N);
		for (int t = 0, stk = N; t < N; t++) {
			if (stk == N) {
				assert(seg[1].val == 0);
				toposort[--stk] = seg[1].ind;
			}

			int cur = toposort[stk];
			//std::cerr << "trying " << cur << '\n';

			int prev_taken = -1;
			int lo = cur-(K-1), hi = cur;
			if (lo < 0) {
				lo += 2 * N, hi = 2 * (N + hi);
			} else {
				lo += N, hi += N;
			}

			downdate_all(lo);
			downdate_all(hi);

			int zero_ind = -1;
			for (int a = lo, b = hi; a < b; a >>= 1, b >>= 1) {
				if (a & 1) {
					if (seg[a].val == 0) {
						zero_ind = seg[a].ind;
						goto found_zero;
					}
					prev_taken = max(prev_taken, seg[a].last_taken);
					a++;
				}
				if (b & 1) {
					--b;
					if (seg[b].val == 0) {
						zero_ind = seg[b].ind;
						goto found_zero;
					}
					prev_taken = max(prev_taken, seg[b].last_taken);
				}
			}
			goto not_found_zero;
found_zero:
			{
				//std::cerr << "bad: " << zero_ind << '\n';
				toposort[--stk] = zero_ind;
				t--;
				continue;
			}
not_found_zero:;
			int next_taken = -1;
			{
				int lo2 = cur+1, hi2 = cur+K;
				assert(lo2 <= N);
				lo2 += N;
				if (hi2 > N) {
					hi2 = 2 * hi2;
				} else {
					hi2 += N;
				}
				for (int a = lo2, b = hi2; a < b; a >>= 1, b >>= 1) {
					if (a & 1) {
						next_taken = max(next_taken, seg[a].last_taken);
						a++;
					}
					if (b & 1) {
						--b;
						next_taken = max(next_taken, seg[b].last_taken);
					}
				}
			}

			assert(toposort[stk] == cur);
			++stk;
			toposort[t] = cur;
			prev_bigger[cur] = (prev_taken == -1) ? -1 : toposort[prev_taken];
			next_bigger[cur] = (next_taken == -1) ? -1 : toposort[next_taken];

			//std::cerr << "take " << cur << ' ' << prev_bigger[cur] << ' ' << next_bigger[cur] << '\n';

			seg[N+cur].last_taken = t;
			seg[N+cur].val += K; // should never be bad
			for (int a = lo, b = hi; a < b; a >>= 1, b >>= 1) {
				if (a & 1) {
					seg[a].lazy--;
					seg[a].val--;
					a++;
				}
				if (b & 1) {
					--b;
					seg[b].lazy--;
					seg[b].val--;
				}
			}
			for (int a = (N+cur) >> 1; a; a >>= 1) { update_node(a); seg[a].last_taken = t; }
			for (int a = lo >> __builtin_ctz(lo) >> 1; a; a >>= 1) update_node(a);
		}

		{
			std::vector<int> pred(2*N+1, -1);
			{
				int cur = 2*N;
				for (int a : toposort) {
					if (a > N-K) {
						pred[cur] = N+a;
						cur = N+a;
					}
				}
			}
			for (int i = 2*N-K; i >= 0; i--) {
				assert(i + K <= 2 * N);
				int j = next_bigger[i >= N ? i-N : i];
				if (j == -1) {
					j = 2 * N;
				} else {
					if (j < i) j += N;
					assert(i < j && j < i + K);
				}
				pred[i] = pred[j];
				pred[j] = i;
			}
			for (int cur = pred[2*N], idx = 2*N-1; idx >= 0; cur = pred[cur], idx--) {
				if (cur >= N) {
					v[cur - N].l_to_r_2 = idx;
				} else {
					v[cur].l_to_r = idx;
				}
			}
		}
		for (int i = 0; i < N; i++) {
			//std::cerr << v[i].l_to_r << ' ' << v[i].l_to_r_2 << '\n';
			assert(v[i].l_to_r > v[i].l_to_r_2);
		}

		{
			std::vector<int> pred(2*N+1, -1);
			{
				int cur = 2 * N;
				for (int a : toposort) {
					if (a < K) {
						pred[cur] = a;
						cur = a;
					}
				}
			}
			for (int i = K; i < 2*N; i++) {
				int j = prev_bigger[i >= N ? i-N : i];
				if (j == -1) {
					j = 2 * N;
				} else {
					if (j + N < i) {
						j += N;
					}
					assert(i - K < j && j < i);
				}
				pred[i] = pred[j];
				pred[j] = i;
			}
			for (int cur = pred[2*N], idx = 2*N-1; idx >= 0; cur = pred[cur], idx--) {
				if (cur >= N) {
					v[cur - N].r_to_l_2 = idx;
				} else {
					v[cur].r_to_l = idx;
				}
			}
		}
		for (int i = 0; i < N; i++) {
			//std::cerr << v[i].r_to_l << ' ' << v[i].r_to_l_2 << '\n';
			assert(v[i].r_to_l_2 > v[i].r_to_l);
		}
	}

public:
	int compare_plants(int x, int y) const {
		if (x == y) return 0;
		if (x > y) {
			return -compare_plants(y, x);
		}
		assert(x < y);
		const auto& a = v[x];
		const auto& b = v[y];
		if (a.l_to_r < b.l_to_r) {
			return -1;
		}
		if (b.l_to_r < a.l_to_r_2) {
			return 1;
		}
		if (a.r_to_l_2 < b.r_to_l) {
			return -1;
		}
		if (b.r_to_l < a.r_to_l) {
			return 1;
		}

		return 0;
	}
} SOLVER;

}

void init(int K, std::vector<int> R) {
	int N = int(R.size());
	SOLVER = plants_solver(N, K, R);
}

int compare_plants(int x, int y) {
	return SOLVER.compare_plants(x, y);
}
#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...