Submission #612381

#TimeUsernameProblemLanguageResultExecution timeMemory
612381erekleCarnival Tickets (IOI20_tickets)C++17
41 / 100
758 ms59864 KiB
#include "tickets.h"
#include <vector>
#include <algorithm>
#include <cassert>

using namespace std;

const long long INF = 1e18;

long long find_maximum(int k, vector<vector<int>> x) {
	int n = x.size();
	int m = x[0].size();

	vector<vector<int>> answer(n, vector<int>(m, -1));
	long long prize = 0;

	int MAX = 0;
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n; ++j) {
			MAX = max(MAX, x[i][j]);
		}
	}

	if (k == 1) {
		vector<vector<long long>> dp(1+n, vector<long long>(1+n/2, -INF));
		dp[0][0] = 0;
		// dp on prefix and number of positives
		vector<vector<bool>> chosePositive(1+n, vector<bool>(1+n/2));
		// chosePositive allows backtracking
		for (int i = 0; i < n; ++i) {
			for (int pos = 0; 2*pos <= n; ++pos) {
				if (dp[i][pos] == -INF) continue;

				// do not choose positive
				long long v = dp[i][pos] - x[i].front();
				if (v > dp[i+1][pos]) {
					dp[i+1][pos] = v;
					chosePositive[i+1][pos] = false;
				}

				// choose positive (if possible)
				if (2*pos == n) continue; // no more positives
				v = dp[i][pos] + x[i].back();
				if (v > dp[i+1][pos+1]) {
					dp[i+1][pos+1] = v;
					chosePositive[i+1][pos+1] = true;
				}
			}
		}

		prize = dp[n][n>>1];
		// Now reconstruct answer
		for (int i = n, pos = n>>1; i >= 1; --i) {
			if (!chosePositive[i][pos]) answer[i-1][0] = 0;
			else answer[i-1][m-1] = 0, pos--;
		}
	} else if (m == k) {
		vector<int> cnt[2], remain[2];
		cnt[0].resize(n), cnt[1].resize(n);
		remain[0].resize(n), remain[1].resize(n);

		vector<pair<int, int>> v;
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j) v.emplace_back(x[i][j], i);
		}
		sort(v.begin(), v.end());
		for (int i = 0; (i<<1) < n*m; ++i) {
			++cnt[0][v[i].second];
		}
		for (int i = 0; i < n; ++i) {
			cnt[1][i] = m-cnt[0][i];
			remain[0][i] = cnt[0][i];
			remain[1][i] = cnt[1][i];
		}

		for (int i = 0; i < k; ++i) {
			int used0 = 0;
			for (int y = 0; y < n; ++y) {
				if (!remain[1][y]) { // zero
					prize -= x[y][cnt[0][y]-remain[0][y]];
					answer[y][cnt[0][y]-remain[0][y]] = i;
					--remain[0][y];
					++used0;
				} else if (!remain[0][y]) { // one
					prize += x[y][cnt[0][y]+remain[1][y]-1];
					answer[y][cnt[0][y]+remain[1][y]-1] = i;
					--remain[1][y];
				}
			}
			for (int y = 0; y < n; ++y) { // both
				if (!remain[0][y] || !remain[1][y]) continue;
				if (2*used0 < n) {
					prize -= x[y][cnt[0][y]-remain[0][y]];
					answer[y][cnt[0][y]-remain[0][y]] = i;
					--remain[0][y];
					++used0;
				} else {
					prize += x[y][cnt[0][y]+remain[1][y]-1];
					answer[y][cnt[0][y]+remain[1][y]-1] = i;
					--remain[1][y];
				}
			}
		}
	} else if (MAX == 1) {
		int z = 0, o = 0;
		vector<int> got0(n), got1(n);
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j) {
				if (x[i][j] == 0) ++z, ++got0[i];
				else ++o, ++got1[i];
			}
		}
		// have to remove (m-k)*n
		int zTarget = 0, rem = (m-k)*n;
		zTarget = (rem+(z-o))/2;
		int mnRemove = 0, mxRemove = 0; // zeros removed
		vector<int> mn(1+n), mx(1+n);
		for (int i = 0; i < n; ++i) {
			mnRemove += max(m-k-got1[i], 0);
			mxRemove += min(m-k, got0[i]);
			mn[i+1] = mnRemove;
			mx[i+1] = mxRemove;
		}

		// Recalculate realistic target
		if (zTarget < mnRemove) zTarget = mnRemove;
		if (zTarget > mxRemove) zTarget = mxRemove;

		// Remove so that target is achieved
		vector<int> removed0(n), removed1(n);
		for (int i = n; i >= 1; --i) {
			int least = max(m-k-got1[i-1], 0);
			int most = min(m-k, got0[i-1]);
			int largestAfter = mx[i-1];
			removed0[i-1] = max(zTarget-largestAfter, least);
			assert(least <= removed0[i-1] && removed0[i-1] <= most);
			removed1[i-1] = m-k-removed0[i-1];
			zTarget -= removed0[i-1];
		}

		// This is the same as the solution to m=k except one detail below
		vector<int> cnt[2], remain[2];
		cnt[0].resize(n), cnt[1].resize(n);
		remain[0].resize(n), remain[1].resize(n);

		vector<pair<int, int>> v;
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j) v.emplace_back(x[i][j], i);
		}
		sort(v.begin(), v.end());
		for (int i = 0; (i<<1) < n*m; ++i) {
			++cnt[0][v[i].second];
		}
		for (int i = 0; i < n; ++i) {
			cnt[1][i] = m-cnt[0][i];
			remain[0][i] = cnt[0][i];
			remain[1][i] = cnt[1][i];
			// Here we are subtracting removed from remain
			remain[0][i] -= removed0[i];
			remain[1][i] -= removed1[i];
			assert(remain[0][i] >= 0 && remain[0][i] <= cnt[0][i]);
			assert(remain[1][i] >= 0 && remain[1][i] <= cnt[1][i]);
		}

		for (int i = 0; i < k; ++i) {
			int used0 = 0;
			for (int y = 0; y < n; ++y) {
				if (!remain[1][y]) { // zero
					prize -= x[y][cnt[0][y]-remain[0][y]];
					answer[y][cnt[0][y]-remain[0][y]] = i;
					--remain[0][y];
					++used0;
				} else if (!remain[0][y]) { // one
					prize += x[y][cnt[0][y]+remain[1][y]-1];
					answer[y][cnt[0][y]+remain[1][y]-1] = i;
					--remain[1][y];
				}
			}
			for (int y = 0; y < n; ++y) { // both
				if (!remain[0][y] || !remain[1][y]) continue;
				if (2*used0 < n) {
					prize -= x[y][cnt[0][y]-remain[0][y]];
					answer[y][cnt[0][y]-remain[0][y]] = i;
					--remain[0][y];
					++used0;
				} else {
					prize += x[y][cnt[0][y]+remain[1][y]-1];
					answer[y][cnt[0][y]+remain[1][y]-1] = i;
					--remain[1][y];
				}
			}
		}
	}
	allocate_tickets(answer);
	return prize;
}
#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...