Submission #1343538

#TimeUsernameProblemLanguageResultExecution timeMemory
1343538madamadam3고대 책들 (IOI17_books)C++20
0 / 100
2133 ms1033848 KiB
#include "books.h"
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
using pi = pair<int, int>;
typedef long long ll;

struct state {
	int hold, pos; string arr; 
	vi hist;
	state(int hold, int pos, string arr) : hold(hold), pos(pos), arr(arr) {}
	const bool operator<(const state &other) const {
		if (arr != other.arr) return arr < other.arr;
		if (hold != other.hold) return hold < other.hold;
		return pos < other.pos;
	}
};

ll brute(vi p, int s) {
	int n = p.size();
	string target = ""; for (int i = 0; i < n; i++) target += 'a' + i;
	string start = ""; for (auto i : p) start += 'a' + i;

	state begin(-1, s, start);
	priority_queue<pair<int, state>, vector<pair<int, state>>, greater<pair<int, state>>> q; set<state> seen;
	q.push({0, begin}); seen.insert(begin);

	ll ans = -1;
	while (!q.empty()) {
		int moves = q.top().first; auto st = q.top().second;
		q.pop();

		// cout << "moves: " << moves << " arr: " << st.arr << " pos: " << st.pos << " hold: " << st.hold << "\n";

		if (st.arr == target && st.pos == s && st.hold == -1) {
			ans = moves;
			vi hist = st.hist;
			for (auto e : hist) {
				if (e == 0) {
					cout << "R";
				} else if (e == 1) {
					cout << "L";
				} else if (e == 2) {
					cout << "S";
				}
			}
			break;
		}

		string ifswap = st.arr; int h2 = st.hold;
		if (st.hold != -1) {
			ifswap[st.pos] = 'a' + st.hold;
			if (st.arr[st.pos] == '#') h2 = -1;
			else h2 = st.arr[st.pos] - 'a';
		} else {
			ifswap[st.pos] = '#';
			h2 = st.arr[st.pos] - 'a';
		}
		auto st2 = state(h2, st.pos, ifswap);
		st2.hist = st.hist; st2.hist.push_back(2);
		if (!seen.count(st2)) seen.insert(st2), q.push({moves, st2});

		if (st.pos < n-1) {
			auto st1 = state(st.hold, st.pos+1, st.arr);
			st1.hist = st.hist; st1.hist.push_back(0);
			if (!seen.count(st1)) seen.insert(st1), q.push({moves+1, st1});
		}
		if (st.pos > 0) {
			auto st1 = state(st.hold, st.pos-1, st.arr);
			st1.hist = st.hist; st1.hist.push_back(1);
			if (!seen.count(st1)) seen.insert(st1), q.push({moves+1, st1});
		}
	}
	cout << "\n";
	return ans;
}

/*
(1) it is absolutely forced to pay at least Σ|p[i] - i| for all i, that is the cost of moving every element into the right spot
(2) note that every permutation cycle should just be followed. the `range` of a cycle = {coord of first el, coord of last el}
	if we encounter another cycle that overlaps with our current one, we can follow it for free, because we would have to do it
	anyways, AND following it puts us back in the exact same spot, so we don't even notice the interruption
(3) so this implies that every overlapping range can be merged (s=0 subtasks), and then the cost is only the cost of travelling between
    disconnected cycles. cost for that = 2*[R[j]-L[i]], because have to walk backwards again (s=0). now. this directly solves the s=0 cases,
	worth 50/100
(4) for arbitrary s, first note that the "active range" of the permutation is [x,y] where x is the first index where p[i] != i, and y is the last
	clearly if s is outside of the active range, we need to walk into it and then back out of it. so ans += 2*min(|La - s|, |Ra - s|)
	you want to determine the cost of merging all cycles within the active range. so. set your borders to [s, s]. while leftmost[L] < L
	L--, and while (rightmost[R]) > R: R++. now try and expand outside of your range. you are looking for a cycle on the left that
	extends past R, or one on the right that extends past L. if you find one on either side, pay 2*min(left dist, right dist). if you
	only find one, pay 2*(dist). if you find none, pay 2*(left+right) and terminate
*/

ll test(vi p, int s) {
	int n = p.size();
	ll ans = 0; for (int i = 0; i < n; i++) ans += abs(i-p[i]);
	
	int fl = 0, fr = n-1;
	while (fl < n && p[fl] == fl) fl++;
	while (fr >= 0 && p[fr] == fr) fr--;

	if (fl >= n || fr < 0) return 0;
	if (s <= fl) ans += 2*abs(fl-s), s = fl;
	else if (fr <= s) ans += 2*abs(fr-s), s = fr;
	
    vi checked(n, 0);
    vi left(n), right(n);
    for (int i = 0; i < n; i++) {
        if (checked[i]) continue;
        int L = i, R = i, x = i;
        vi cycle;
        while (!checked[x]) {
            checked[x] = 1; cycle.push_back(x);
            L = min(L, x); R = max(R, x);
            x = p[x];
        }
        for (int node : cycle) { left[node] = L; right[node] = R; }
    }

    for (int i = 0; i < n; i++) {
        int curL = left[i], curR = right[i];
        for (int j = curL; j <= curR; j++) {
            curL = min(curL, left[j]);
            curR = max(curR, right[j]);
        }
        left[i] = curL; right[i] = curR;
    }

	int L = left[s], R = right[s];
	while (!(L <= fl && R >= fr)) {
		int xl = L, xr = R, costl = 0; bool foundx = false;
		while (xl > fl && xr <= R) {
			xl--; costl += 2;
			xl = min(xl, left[xl]); xr = max(xr, right[xl]);
			foundx = xr > R;
		}
 
		int yl = L, yr = R, costr = 0; bool foundy = false;
		while (yr < fr && yl >= L) {
			yr++; costr += 2;
			yl = min(yl, left[yr]); yr = max(yr, right[yr]);
			foundy = yl < L;
		}

		if (foundx && foundy) {
			ans += min(costl, costr);
			L = min(xl, yl), R = max(xr, yr);
		} else if (foundx) {
			ans += costl, L = xl, R = xr;
		} else if (foundy) {
			ans += costr, L = yl, R = yr;
		} else {
			ans += costl, L = fl;
			ans += costr, R = fr;
		}
	}
	return ans;
}

ll minimum_walk(vi p, int s) {
	// return test(p, s);
	// return brute(p, s);
	vector<int> perm(4); iota(perm.begin(), perm.end(), 0);
	do {
		int a = brute(perm, 2), b = test(perm, 2);
		if (a != b) {
			cout << "FAILED: on n = 4, s = 0, p = "; for (auto e : perm) cout << e << " "; cout << "\n";
			cout << "brute: " << a << " test: " << b << "\n";
		}
	} while (next_permutation(perm.begin(), perm.end()));
	return brute(p, s);
}
#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...