알고리즘

Codeforces Round #687 (Div.2)

39dll 2020. 11. 30. 05:15

어제 있었던 코드포스 라운드 687 풀이이다. 3솔브를 했고, D를 업솔브했다.

 

A. Prison Break

\(max( r + c - 2, m - r + c - 1,n - c + r - 1,m - r + n - c )\)가 답이다.

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(0); cin.tie(0);
	int T; cin >> T;
	while (T--) {
		long long m, n, r, c;
		cin >> m >> n >> r >> c;
		long long mx = max({ r + c - 2, m - r + c - 1,n - c + r - 1,m - r + n - c });
		cout << mx << '\n';
	}
}

 

 

 

 

B. Repainting Street

\(c_i \le 100\)이라는 점에 착안해, 답을 \([1, 100]\)의 한 정수로 고정하고, 모든 답에 대해 다 칠해보는 전략을 생각해볼 수 있다. 칠하는 것은 앞에서부터 탐색하며 칠하는 간단한 그리디로 해결할 수 있다.

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(0); cin.tie(0);
	int T; cin >> T;
	while (T--) {
		long long N, K;
		cin >> N >> K;
		vector<long long> v(N);
		for (auto &i : v)cin >> i;
		int ans = 210000000;
		for (int i = 1; i <= 100; i++) {
			auto temp = v;
			int cnt = 0;
			for (int j = 0; j < N; j++) {
				if (temp[j] != i) {
					for (int k = 0; k < K; k++) {
						if (j + k < N)temp[j + k] = i;
					}
					cnt++;
				}
			}
			ans = min(ans, cnt);
		}
		cout << ans << '\n';
	}
}

 

 

 

 

C. Bouncing Ball

먼저, 맨 앞 블록을 삭제하고 뒤의 블록을 한 칸 씩 당겨오는 행동은 단순히 \(p\)를 \(1\) 증가시키는 것과 같다는 점을 관찰한다. 또한, \(p\)를 \(k\)번 증가시키면 설치해야 하는 블록의 개수는 증가시키기 전에서 맨 앞 블록을 신경쓰지 않는 것과 같다는 점도 관찰할 수 있다. 따라서, \(p_0 + ck\)인 모든 수에 대해 Prefix Sum을 이용하면 각 케이스를 \(O(1)\)에 판별 가능하다(단, \(c\)는 임의의 정수). 이것을, \(p \le p_0 < p+k\)에 대해서 반복하면 된다.

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(0); cin.tie(0);
	int T; cin >> T;
	while (T--) {
		long long n, p, k, x, y;
		cin >> n >> p >> k;
		string s; cin >> s;
		cin >> x >> y;
		long long mn = 21000000000000000;
		for (int i = 0; i < k; i++) {
			long long init = i * y;
			vector<int> psum0;
			psum0.push_back(0);
			for (int q = p + i - 1; q < n; q += k) {
				psum0.push_back(psum0.back() + (s[q] == '0'));
			}
			for (int ind = 1; ind < psum0.size(); ind++) {
				long long cost = init + (psum0.back() - psum0[ind - 1]) * x + y*k*(ind - 1);
				mn = min(mn, cost);
			}
			
		}
		cout << mn << '\n';
	}
}

 

 

 

 

D. XOR-gun

 

먼저, 두 가지 사실을 관찰할 수 있다.

1. 2진수의 자리수가 같은 두 수를 XOR하면 무조건 자리수가 줄어든다.

2. 답은 무조건 \([x, y], [y+1,z]\)의 연속된 두 구간을 각각 XOR했을 때 두 수가 대소관계가 바뀔 때만 나온다.

 

1번 조건으로부터, 같은 자리수가 3개 이상일 때 답이 무조건 \(1\)임을 알 수 있다. 따라서 \(N>30\)인 모든 케이스의 답은 무조건 \(1\)이다. 또한 2번 조건으로부터, \(N \le 30\)일 때 \(O(N^3)\) 또는 \(O(N^4)\) 솔루션을 얻을 수 있다.

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(0); cin.tie(0);
	long long N; cin >> N;
	vector<long long>v(N), pw2(33);
	long long k = 1;
	for (int i = 0; i < 32; i++) {
		pw2[i] = k; k <<= 1;
	}
	for (auto &i : v)cin >> i;
	auto fd = [&](long long val)->long long {
		for (int i = 31; i >= 0; i--) {
			if (val >= pw2[i]) {
				return i;
			}
		}
	};
	long long prev = fd(v[1]);
	long long prev2 = fd(v[0]);
	for (int i = 2; i < N; i++) {
		long long cur = fd(v[i]);
		if (prev == prev2 && prev == cur) {
			cout << 1;
			return 0;
		}
		prev2 = prev;
		prev = cur;
	}
	long long ans = 2100000000;
	for (int i = 0; i < N; i++) {
		long long one = v[i];
		for (int j = i; j < N; j++) {
			for (int p = i + 1; p <= j; p++) {
				one ^= v[p];
			}
			for (int k = j + 1; k < N; k++) {
				long long two = v[k];
				for (int p = k - 1; p >= j + 1; p--) {
					two ^= v[p];
				}
				if (one > two) {
					ans = min(ans, (long long)k - i - 1);
				}
			}
		}
	}
	cout << (ans == 2100000000 ? -1 : ans);
}

 

 

여담.

D 풀이에 되게 근접했는데 틀려서 너무 아쉽다. 하지만 부계정도 블루에 안착시키는 데에 성공했다.

 

부계정도 블루 달성