프로그래밍 연습장

Codeforces Round #461 (Div. 2) 본문

문제 해결/codeforces.com

Codeforces Round #461 (Div. 2)

김준원 2018. 2. 9. 15:27

오늘부터는 참가한 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+1y 이면 가능하다.


시간복잡도: O(1)

개인적으로는 이번 A와 같은 단순 hack 유도 문제를 정말로 싫어한다. 몇 가지 이상한 케이스들만 찾으면 E를 푼 것보다 많은 점수를 얻을 수 있다는 것이 너무나 불합리하다.


B. Magic Forest


문제 조건 axorbxorc=0axorb=c이기에, a, b에 대해서만 루프를 돌면서 c를 찾아주고, 조건에 맞는지 확인해준다.


시간복잡도: O(n2)


C. Cave Painting


얼핏 봐서는 1에서 k까지의 모든 modulo에 대해서 나누어야 할 것 같지만, 실제로 계산해야 하는 k는 훨씬 작아진다. n1에서 k까지의 모든 수로 나누었을 때의 나머지가 다르려면,


1로 나누었을 때의 나머지는, 0만 가능하다.

2로 나누었을 때의 나머지는, 0을 이미 썼기에, 1만 가능하다.

3으로 나누었을 때의 나머지는, 0, 1을 이미 썼기에, 2만 가능하다.

k로 나누었을 때의 나머지는, 0, , k2를 이미 썼기에, k1만 가능하다.


즉, 1에서 k까지의 수로 나누었을 때의 나머지가 모두 다르려면, nlcm(1,,k)1여야 한다. n=1018일 때 k37이라고 한다. 정확한 수치를 몰라도 1부터 겹치는 나머지가 발견될 때까지 반복해 돌리면 최대 i=37에서 반복이 종료된다는 뜻이다.


시간복잡도: f(n):=lcm(1,,n)일 때, O(f1(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+1haisaihai+1이다.


sai+1haisaihai+1<0haisai<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) 정도 (구현에 따라 다름)


풀이를 보고 나서도 찝찝한 문제이다.

Comments