일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 코드포스
- 2794
- BAPC
- 컨벡스 헐
- 백준 온라인 저지
- DP
- 기하 알고리즘
- 14179
- ICPC 연습
- 구현
- 8192
- codeforces
- JOI
- 기하
- IOI 2013
- boj
- convex hull
- Z algorithm
- 볼록 껍질
- acmicpc.net
- JOI 2018
- BAPC 2018
- Manacher's algorithm
- Z 알고리즘
- 그래프
- POI Solution
- Manacher 알고리즘
- 세그먼트 트리
- Implementation
- Divide and conquer optimization
- Today
- Total
프로그래밍 연습장
Codeforces Round #461 (Div. 2) 본문
오늘부터는 참가한 codeforces의 풀이도 한 번 올려보려 한다. Round #461은 내가 부 계정으로 참가한 첫 대회였다. 나는 대회 끝까지 핵을 찾지 못한 A와, F를 제외한 B, C, D, E를 대회 시간 내에 풀었다.
A. Cloning Toys
Case-by-Case가 정해이다. 문제의 설명문의 조건이 이상해서, (x,y)=(1,0),(0,1),(2,1) 등의 찾기 힘든 핵이 많이 등장했다. 특히 (2,1) 케이스. 풀이는 다음과 같다.
i) y=0: 불가능하다.
ii) y=1: x=0인 경우에만 가능하다. x>0이어야 x를 클론하는 연산을 할 수 있기 때문이다...
iii) y>1: 초기 (0,1)의 상태에서 무조건 (x,y)→(x+1,y+1),(x+2,y)로만 갈 수 있기 때문에 ① x, y의 홀짝성이 다르고, ② x+1≥y 이면 가능하다.
시간복잡도: O(1)
개인적으로는 이번 A와 같은 단순 hack 유도 문제를 정말로 싫어한다. 몇 가지 이상한 케이스들만 찾으면 E를 푼 것보다 많은 점수를 얻을 수 있다는 것이 너무나 불합리하다.
B. Magic Forest
문제 조건 axorbxorc=0⟺axorb=c이기에, a, b에 대해서만 루프를 돌면서 c를 찾아주고, 조건에 맞는지 확인해준다.
시간복잡도: O(n2)
C. Cave Painting
얼핏 봐서는 1에서 k까지의 모든 modulo에 대해서 나누어야 할 것 같지만, 실제로 계산해야 하는 k는 훨씬 작아진다. n을 1에서 k까지의 모든 수로 나누었을 때의 나머지가 다르려면,
□ 1로 나누었을 때의 나머지는, 0만 가능하다.
□ 2로 나누었을 때의 나머지는, 0을 이미 썼기에, 1만 가능하다.
□ 3으로 나누었을 때의 나머지는, 0, 1을 이미 썼기에, 2만 가능하다.
□ ⋯
□ k로 나누었을 때의 나머지는, 0, ⋯, k−2를 이미 썼기에, k−1만 가능하다.
즉, 1에서 k까지의 수로 나누었을 때의 나머지가 모두 다르려면, n≥lcm(1,⋯,k)−1여야 한다. n=1018일 때 k≤37이라고 한다. 정확한 수치를 몰라도 1부터 겹치는 나머지가 발견될 때까지 반복해 돌리면 최대 i=37에서 반복이 종료된다는 뜻이다.
시간복잡도: f(n):=lcm(1,⋯,n)일 때, O(f−1(n)). f(x)≤x!에서 O(lognloglogn)도 가능.
D. Robot Vacuum Cleaner
총 noise = 조각 문자열 안의 noise + 조각 문자열들 사이의 noise
왼쪽 항은 고정이므로, 오른쪽 항만 생각하자. 이 때 각 조각 문자열들의 s와 h의 개수만이 영향을 미친다. 조각 문자열들의 어떤 배열 ta1,⋯,tan이 최적이려면, 어떤 인접한 두 문자열을 바꾸어도 (ta1,⋯tai+1,tai,⋯,tan으로 바꾼다) noise가 줄어들어야 한다. 역은 성립하지 않는다.
ti 안의 s의 개수를 si, h의 개수를 hi라고 하면, tai,tai+1에서 tai+1,tai로 바꿀 때 증가하는 noise는 sai+1hai−saihai+1이다.
sai+1hai−saihai+1<0⟺haisai<hai+1sai+1
라는 것은, hisi의 오름차순으로 ti를 정렬하였을 때, 정렬된 순서가 아닌 배치라면, 인접한 두 문자열을 바꾸어서 noise를 증가시킬 수 있고, 따라서 최적이 아님을 의미한다. 정렬된 순서가 최적임은 직접적으로 증명하지 않았으나, 정렬되지 않은 모든 순서가 최적이 아님을 증명하였기에, 자명히 정렬된 순서가 최적이다.
따라서, hisi의 오름차순으로 ti를 정렬한 후 이어붙여, 왼쪽부터 차례대로 훑으며 noise를 계산하면 된다. 이어붙인 문자열의 각 h에 대해, 왼쪽에 있는 s의 개수를 합하면 계산 가능하다. 64비트 정수형을 써야 함에 유의하자.
시간 복잡도: O(∑l+nlogn)
E. Birds
dp(i,j) := i번째 나무까지 j마리의 새를 잡았을 때, 최대로 남길 수 있는 마나
로 정의하자. 만약 j마리의 새를 i번째 나무까지 잡을 수 없다고 할 때는, dp(i,j)=−inf로 정의하자. 이를 정의하지 않으면, 마나가 음수로 갔다가 다시 양수로 돌아오는 상태를 유효하다고 판단할 수도 있다. dp(0,0)=W이 되고, 점화식은 다음과 같이 정의된다.
dp(i,j)=max
이 식에서, j와는 무관하고 i-1, k와만 관련이 있는 식을 다음과 같이 분리해보자.
f(i, k) :=\min(dp(i, k) + X, W + Bk) + k\operatorname{cost}(i)
이렇게 f를 분리하면, dp의 점화식은 다음과 같이 간단하게 바뀐다.
dp(i, j) = \max _ {j-c_i \le k \le j} f(i-1, k) - j\operatorname{cost}(i)
이제, deque를 이용한 잘 알려진 슬라이딩 윈도우 기법으로 이를 최적화할 수 있다. 풀이는 n개의 phase로 이루어질 것인데, 각 phase에서는 dp(i, *)를 계산할 것이다. 각 phase는 # 이 문제에서 a_k = f(i-1, k)인 형태를 풀면 된다. 각 계산 단계에서 dp(i, j)<0이면 dp(i, j) = -inf를 처리해주는 것을 잊지 않도록 하자.
답은, dp(n, k) \ge 0인 최대의 k가 된다.
시간 복잡도: O(n \sum c_i )
F. Divisibility
공식 editorial에 따르면, well-guessing(야매)이 정해인 듯 하다. 전체집합 {1, 2, \cdots, n}에서 배수 관계인 쌍의 개수가 정확히 k개가 될 때까지 적절한 규칙으로 빼준다. 물론, 전체집합의 배수 쌍이 k개 미만이라면 무조건 불가능하다. 공식 풀이는 다음과 같이 하면 무조건 가능함을 주장하고 있다.
i) 쌍의 개수가 k개 이상이 유지되는 선 안에서, 맨 끝 수를 뺀다. n을 이 과정을 끝냈을 때의 전체집합의 크기로 재설정하자.
ii) 이 때, n \over 2 이상의 수들만 적절히 빼도 무조건 k개인 해를 얻을 수 있다. 빼는 방식은 연결된 쌍의 수가 많은 것부터 빼던가 알아서 해도 된다.
증명은, n이 적절히 커지면 n \over 2 이상의 소수 개수 O({n \over \log n})이 빼야 하는 쌍의 수인 O(n^{1 over 3})보다 많아진다는 사실로 대신한다고 한다. 그렇지 않은 예외 케이스 16개 모두 ii)의 방법으로 풀 수 있다고 한다.
시간 복잡도: O(n \log n) 정도 (구현에 따라 다름)
풀이를 보고 나서도 찝찝한 문제이다.