본문 바로가기

알고리즘

Codeforces Round #689 (Div.2)

4솔브

이틀 전에 있었던 코드포스 라운드 689의 풀이이다. 개인적으로 적절한 그림 등과 함께 깔끔한 문제들이 나왔던 것 같다. 다만, B 문제의 제한을 좀 더 작게 줬으면 더 직관적이었을 것 같다.

 

 

 

A. String Generation

 

단순히 길이가 \(N\)인 \(abcabc...\) 문자열을 출력해주면 된다.

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while (T--) {
		long long n, k; cin >> n >> k;
		for (int i = 0; i < n; i++) {
			if (i % 3 == 0)cout << "a";
			if (i % 3 == 1)cout << "b";
			if (i % 3 == 2)cout << "c";
		}cout << '\n';
	}
}

 

 

 

 

B. Find the Spruce

 

일단, 문제 자체는 이 문제와 동일하다. 그런데, 제한이 \(N \le 500\)으로 더 작다. 나는 일단 이 문제가 \(O(N^3)\)으로 안 풀린다고 생각해서 \(O(N^2logN)\)으로 어렵게 풀었다. 나중에 알아보니 보통 \(N \le 500\)정도일 때는 \(O(N^3)\)이 돌아간다고 한다. 출제자의 의도를 잘 파악해야겠다고 느꼈다.

 

내가 푼 방식은 먼저 각 행에 대해 Prefix Sum을 구해놓고, 하나의 열을 잡아 고정시킨 뒤 위에서부터 아래로 내려가며 Prefix Sum을 이용해 이분 탐색하는 방식이다. 이 과정에서 평소의 버릇대로 Prefix Sum을 1-based로 만들어놨는데, 이걸 실수로 0-based로 처리해서 디버깅하는데 약 20분이 걸렸다. 이런 실수를 좀 줄여야겠다.

 

어렵지 않게 구현할 수 있는 DP로도 이 문제를 풀 수 있다.

 

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while (T--) {
		int N, M; cin >> N >> M;
		vector<string>v;
		for (int i = 0; i < N; i++) {
			string in; cin >> in;
			v.push_back(in);
		}
		vector<vector<int>> psum;
		for (int i = 0; i < N; i++) {
			vector<int> cur(M + 1);
			for (int j = 0; j < M; j++) {
				cur[j + 1] = cur[j] + (v[i][j] == '*');
			}
			psum.push_back(cur);
		}
		long long ans = 0;
		for (int j = 0; j < M; j++) {
			int cur = -1;
			for (int i = 0; i < N; i++) {
				if (v[i][j] == '.') {
					cur = -1;
					continue;
				}
				int st = 0, en = cur + 1;
				int mx = 0;
				while (st <= en) {
					int mid = (st + en) / 2;
					if (mid + j + 1 > M) {
						en = mid - 1;
						continue;
					}
					if (j - mid < 0) {
						en = mid - 1;
						continue;
					}
					if (psum[i][j + mid + 1] - psum[i][j - mid] == mid * 2 + 1) {
						st = mid + 1;
						mx = mid;
					}
					else {
						en = mid - 1;
					}
				}
				cur = mx;
				ans += mx + 1;
			}
		}
		cout << ans << '\n';
	}
}

 

 

 

 

C. Random Events

 

흔하지 않은 유형인 확률을 이용하는 문제이다. 풀이는 간단한데, 정렬되어야 하는 가장 인덱스가 큰 원소를 구하고, 그 인덱스까지 영향을 주는 쿼리만 모아서 1에서 해당 확률을 빼준 뒤 전부 곱해준 뒤에, 1에서 그 값을 빼주면 답이다.

 

이 문제를 풀 때 주의해야 하는 점이 하나 있는데, 이미 정렬이 되어 있으면 답이 무조건 1인데, 그렇다고 1을 출력하고 바로 continue를 해버리면 인풋이 꼬여버린다는 점이다. 꼭 쿼리 인풋도 다 받도록 하자.

 

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, double> pid;
int main() {
   ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
   int T; cin >> T;
   while (T--) {
      int n, q, tl = -1; cin >> n >> q;
      vector<int> perm(n); for (auto& i : perm) cin >> i;
      vector<pid> query;
      for (int i = n - 1, cnt = 0; i >= 0; i--, cnt++) {
         if (perm[i] != n - cnt) {
            tl = n - cnt; break;
         }
      }
      for (int i = 0; i < q; i++) {
         int rg; double pr; cin >> rg >> pr;
         if (rg < tl) continue;
         query.push_back({ rg, pr });
      }
      sort(query.begin(), query.end());
      double res = 1;
      for (int i = query.size() - 1; i >= 0; i--) {
         res *= (1 - query[i].second);
      }
      if (tl == -1) {
         cout << 1 << '\n'; continue;
      }
      cout << 1.0 - res << '\n';
   }
}

 

 

 

 

D. Divide and Summerize

 

문제가 요구하는 것은 굉장히 자명하다. 먼저, 문제대로 모든 Segment를 나누면 공간 복잡도가 \(O(NlogN)\)임을 캐치해야 한다. 그러면, 모든 Segment에 대해서 분할 정복으로 문제를 해결할 수 있음을 알 수 있다. 하나의 Segment에 대해 수행해야 할 연산은 아래와 같다.

 

1. Segment의 최댓값, 최솟값을 구해서 평균을 구한다.

2. 평균의 upper_bound를 구한다.

3. upper_bound를 기준으로 좌우로 Segment를 분리한다.

4. Segment를 얻을 때 마다 구간합을 구해 set에 넣는다.

5. 쿼리는 set을 이용하여 \(logN\)에 처리할 수 있다.

 

1번, 4번 연산을 위해 Segment Tree를 사용할 수도 있고, 업데이트가 없으므로 Sparse Table, Prefix Sum등 정말 다양한 풀이를 사용할 수 있다. 나는 구간합, 구간 최댓값, 구간 최솟값을 각각 구할 수 있는 Segment Tree 3개를 만들었다.

 

#include <bits/stdc++.h>
using namespace std;

const long long INF = 2100000000000000;

struct SegTree {
	int sz, st, dep;
	vector<long long> pw2, v;
	SegTree(int size) {
		long long k = 1;
		for (int i = 0; i < 30; i++) pw2.push_back(k), k <<= 1;
		sz = size; v.resize(sz * 4, 0); dep = 1;
		while (1) {
			if (sz <= pw2[dep - 1])break;
			dep++;
		}
		st = pw2[dep - 1] - 1;
	}
	void init(long long val) {
		for (int i = 1; i < 4 * sz; i++) v[i] = val;
	}
	long long val(int ind) {
		ind++;
		return v[st + ind];
	}
	void update(int ind, long long val) {
		ind++;
		v[st + ind] = val; ind = (ind + 1) / 2;
		for (int i = dep - 1; i >= 1; i--) {
			int cur = pw2[i - 1] - 1 + ind;
			v[cur] = v[cur * 2] + v[cur * 2 + 1];
			ind = (ind + 1) / 2;
		}
	}
	long long query(int start, int end) {
		start++; end++;
		long long ret = 0;
		start += st; end += st;
		while (start <= end) {
			if (start % 2 == 1)ret += v[start];
			if (end % 2 == 0)ret += v[end];
			start = (start + 1) / 2;
			end = (end - 1) / 2;
		}
		return ret;
	}
};

struct SegTree2 {
	int sz, st, dep;
	vector<long long> pw2, v;
	SegTree2(int size) {
		long long k = 1;
		for (int i = 0; i < 30; i++) pw2.push_back(k), k <<= 1;
		sz = size; v.resize(sz * 4, -INF); dep = 1;
		while (1) {
			if (sz <= pw2[dep - 1])break;
			dep++;
		}
		st = pw2[dep - 1] - 1;
	}
	void init(long long val) {
		for (int i = 1; i < 4 * sz; i++) v[i] = val;
	}
	long long val(int ind) {
		ind++;
		return v[st + ind];
	}
	void update(int ind, long long val) {
		ind++;
		v[st + ind] = val; ind = (ind + 1) / 2;
		for (int i = dep - 1; i >= 1; i--) {
			int cur = pw2[i - 1] - 1 + ind;
			v[cur] = max(v[cur * 2], v[cur * 2 + 1]);
			ind = (ind + 1) / 2;
		}
	}
	long long query(int start, int end) {
		start++; end++;
		long long ret = 0;
		start += st; end += st;
		while (start <= end) {
			if (start % 2 == 1)ret = max(ret, v[start]);
			if (end % 2 == 0)ret = max(ret, v[end]);
			start = (start + 1) / 2;
			end = (end - 1) / 2;
		}
		return ret;
	}
};

struct SegTree3 {
	int sz, st, dep;
	vector<long long> pw2, v;
	SegTree3(int size) {
		long long k = 1;
		for (int i = 0; i < 30; i++) pw2.push_back(k), k <<= 1;
		sz = size; v.resize(sz * 4, INF); dep = 1;
		while (1) {
			if (sz <= pw2[dep - 1])break;
			dep++;
		}
		st = pw2[dep - 1] - 1;
	}
	void init(long long val) {
		for (int i = 1; i < 4 * sz; i++) v[i] = val;
	}
	long long val(int ind) {
		ind++;
		return v[st + ind];
	}
	void update(int ind, long long val) {
		ind++;
		v[st + ind] = val; ind = (ind + 1) / 2;
		for (int i = dep - 1; i >= 1; i--) {
			int cur = pw2[i - 1] - 1 + ind;
			v[cur] = min(v[cur * 2], v[cur * 2 + 1]);
			ind = (ind + 1) / 2;
		}
	}
	long long query(int start, int end) {
		start++; end++;
		long long ret = INF;
		start += st; end += st;
		while (start <= end) {
			if (start % 2 == 1)ret = min(ret, v[start]);
			if (end % 2 == 0)ret = min(ret, v[end]);
			start = (start + 1) / 2;
			end = (end - 1) / 2;
		}
		return ret;
	}
};

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int T; cin >> T;
	while (T--) {

		int N, Q; cin >> N >> Q;
		SegTree *sg = new SegTree(N + 1);
		SegTree2 *mxsg = new SegTree2(N + 1);
		SegTree3 *mnsg = new SegTree3(N + 1);
		vector<long long> v(N);
		for (int i = 0; i < N; i++) {
			cin >> v[i];
		}
		sort(v.begin(), v.end());
		for (int i = 0; i < N; i++) {
			sg->update(i, v[i]);
			mxsg->update(i, v[i]);
			mnsg->update(i, v[i]);
		}
		set<long long> s;
		queue<pair<int, int>> q;
		q.push({ 0,N - 1 });
		while (!q.empty()) {
			int lf, rg;
			tie(lf, rg) = q.front(); q.pop();
			if (lf > rg)continue;
			s.insert(sg->query(lf, rg));
			if (lf == rg)continue;
			long long mn, mx;
			mn = mnsg->query(lf, rg);
			mx = mxsg->query(lf, rg);
			long long mid = (mn + mx) / 2;

			auto it = upper_bound(v.begin() + lf, v.begin() + rg + 1, mid) - v.begin();
			if (it > rg)continue;
			q.push({ lf,it-1 }); q.push({ it,rg });
		}

		while (Q--) {
			long long in; cin >> in;
			if (s.find(in) != s.end()) {
				cout << "Yes\n";
			}
			else {
				cout << "No\n";
			}
		}
	}
}

 

'알고리즘' 카테고리의 다른 글

Codeforces Global Round 12  (0) 2020.12.07
Codeforces Round #687 (Div.2)  (0) 2020.11.30
Codeforces Round #686 (Div.3)  (0) 2020.11.25
Codeforces Round #685 (Div.2)  (0) 2020.11.24
Educational Codeforces Round 98 (Div.2)  (0) 2020.11.21