본문 바로가기

대회 리뷰/BOJ

2023 KSA Automata Winter Contest

 

2023 KSA Automata Winter Contest

 

www.acmicpc.net

A - 소수가 아닌 수

1e9

00:02 AC

 

B - 그래서 대회 이름 뭐로 하죠

단순 구현

00:07 AC

 

C - 수학 퀴즈

w^2 + w + 1 = 0 이므로 다음이 성립한다.

  • w^2 = -(w + 1)
  • w^3 = -(w^2 + w) = 1
  • w^4 = w

00:16 AC

 

D - 2배 또는 0.5배

조건을 만족하는 수열은 항상 존재한다.

(i) N % 4 == 0

1 2 4 3 | 5 6 8 7 | ... |

(ii) N % 4 == 1

1 2 4 3 | 5 6 8 7 | ... | N

(iii) N % 4 == 2

1 2 4 3 | 5 6 8 7 | ... | (N - 1) N

(iv) N % 4 == 3

1 3 2 4 | 5 7 6 8 | ... | (N - 2) N (N - 1)

00:30 AC

 

에디토리얼 풀이는 매우 간결하다.

(i) N % 2 == 0

2 1 | 3 4 | 6 5 | 7 8 | 10 9 | ... |

(ii) N % 2 == 1

1 | 3 2 | 4 5 | 7 6 | 8 9 | 11 10 | ... |

 

E - 퀸 움직이기

다음과 같은 점화식을 세울 수 있다.

dp[i][j][k][l] = 퀸을 i번 움직였고 마지막 이동이 j번 방향일 때 (k, l) 칸으로 보낼 수 있는 경우의 수

dp 배열을 naive하게 업데이트하면 서브태스크 3까지 맞는다.

서브태스크 4까지 맞으려면 누적 합을 이용하여 효율적으로 업데이트하면 된다.

00:53 AC

 

F - 멋진 부분집합

모든 수를 소인수분해하여 확인하면 서브태스크 2까지 맞는다.

01:19 AC

 

크기가 N / 2인 멋진 집합이 존재한다고 가정하자.

중복집합에서 임의의 원소를 선택하였을 때 해당 원소가 멋진 집합에 속할 확률은 1 / 2이다.

따라서 randomization을 이용하면 높은 확률로 문제를 해결할 수 있다.

01:23 AC

 

2912번에서 배운 테크닉인데 유용하게 잘 사용하였다.

 

G - 시그마 시그마 시그마 시그마

서브태스크 2는 별 도움이 되지 않으므로 생략한다.

다음과 같은 사실을 관찰할 수 있다.

  • i < j일 때 max(A[i], A[j])는 (i * (N - j + 1))번 등장한다.

이를 이용하면 서브태스크 3을 해결할 수 있다.

01:39 AC

 

서브태스크 4까지 맞으려면 위의 식을 더 빠르게 계산해야 한다.

각 원소를 역순으로 보면서 다음을 계산해주자.

  • sum1 = i < j and A[i] <= A[j]인 모든 j에 대하여 (N - j + 1) * A[j]의 합
  • sum2 = i < j and A[i] > A[j]인 모든 j에 대하여 (N - j + 1)의 합

정답은 (i * (sum1 + sum2 * A[i]))만큼 증가한다.

sum1과 sum2는 각각 Fenwick Tree를 이용하여 빠르게 구할 수 있다.

02:02 AC

 

H - 깃발 꽂기

대회 중에 처음으로 푼 다이아 문제이다.

2-sat 문제처럼 보여서 급하게 2-sat을 공부하고 왔다.

아무튼 2-sat 문제로 모델링하면 서브태스크 2까지 맞는다.

03:13 AC

 

구체적으로는 다음과 같이 모델링할 수 있다.

  • 지상 통로가 연결하는 두 정점 u와 v에 대하여 (u ∨ v)를 추가한다.
  • 구름다리로만 구성된 그래프를 G라고 하자.
  • G에서 동일한 연결 요소에 속하는 두 정점 u와 v에 대하여 (¬u ∨ ¬v)를 추가한다.

구름다리 모델링 과정에서 최대 O(N^2)의 간선이 추가되기 때문에 이 방법으로는 서브태스크 3을 해결할 수 없다.

 

위와 같은 상황을 At-Most-One Constraint라고 한다.

정해에서는 이를 위한 특수한 모델링 기법을 사용한다.

여러 기법이 존재하지만 간편하게 사용할 수 있는 것은 다음과 같이 두 가지이다.

  • O(N)개의 추가 정점과 O(N)개의 간선을 사용하는 Ladder Encoding
  • O(log N)개의 추가 정점과 O(N log N)개의 간선을 사용하는 Binary Encoding

얼핏 보면 간선의 개수가 적은 Ladder Encoding이 빠를 것 같지만 항상 그렇지는 않다.

아무튼 이러한 기법으로 간선의 개수를 줄이면 서브태스크 3까지 해결할 수 있다.

 

하지만 나는 대회 당일 2-sat을 처음 배웠고 이러한 기법을 알고 있을 리가 없다.

지금부터는 나의 꼼수 풀이를 소개한다.

나는 scc를 구할 때 kosaraju 알고리즘을 이용한다.

tarjan 알고리즘을 공부한 지 오래되어 동일하게 적용할 수 있는지는 모르겠다.

 

각 정점은 반드시 하나의 연결 요소에 속한다.

그리고 동일한 연결 요소에 속하는 두 정점 간에는 반드시 간선이 존재한다.

kosaraju 알고리즘의 동작 과정을 살펴보면 정점의 방문 여부가 중요하다는 것을 알 수 있다.

따라서 이미 방문한 정점을 끝점으로 하는 간선은 고려하지 않아도 된다.

 

위의 관찰로부터 간선을 실제로 구성하는 대신 가상으로 관리하는 아이디어를 떠올릴 수 있다.

어차피 중요한 것은 정점들의 방문 여부이고 모든 간선이 실제로 존재할 필요는 없다.

Union-Find와 queue를 이용하면 간선을 실제로 구성하지 않으면서 본래 방문하려고 한 모든 정점을 방문하도록 할 수 있다.

03:44 AC

 

구현도 더럽고 범용적인 풀이는 아니므로 위에서 설명한 모델링 기법을 공부하는 것을 권장한다.

 

I - 이상한 판 뒤집기 게임 (not solved)

풀이가 예술적이다.

에디토리얼을 참고하자.

 

J - 팬케이크 탑 (not solved)

편의를 위하여 p = p / q라고 재정의한다.

다음과 같은 점화식을 세울 수 있다.

dp[i][j][k] = i개의 팬케이크 중 j개의 팬케이크가 상했고 마지막 팬케이크는 (k ? "상했을" : "상하지 않았을") 확률

이를 계산하면 서브태스크 2까지 맞는데 대회 중에는 틀렸다.

03:57 WA    03:59 WA

 

H번을 풀고 대략 15분 정도가 남았었고 급하게 코딩에 들어 갔다.

급하게 코딩하다 보니 두 가지 잔실수를 범하였고 대회가 종료될 때까지 원인을 찾지 못하였다.

 

첫 번째 실수는 점화식의 계산 과정에서 나왔다.

dp[i][0][0] = dp[i - 1][0][0] * (1 - p) % MOD;

이전 팬케이크가 상하지 않았을 때 다음 팬케이크도 상하지 않았을 확률은 p이다.

따라서 위의 식에서 (1 - p) 대신 p를 사용해야 한다.

 

두 번째 실수도 점화식의 계산 과정에서 나왔는데 연산 도중 답이 음수가 되는 상황이 생겼다.

아래와 같은 문장을 추가하여 이를 방지할 수 있다.

ans = (ans + MOD) % MOD;

정확히 대회 종료 10초 전에 이를 알아차렸는데 10초는 너무 짧은 시간이었다.

어차피 첫 번째 실수를 고치지 않아 WA는 확정이었다.

 

이제 서브태스크 3을 풀어 보자.

각 팬케이크를 위에서부터 차례대로 확인하자.

이때 한 종류의 팬케이크가 (N + 1) / 2번 등장하는 순간 그 아래에 있는 팬케이크의 종류와는 상관 없이 결과는 확정된다.

따라서 문제에 다음과 같은 조건을 추가할 수 있다.

  • 한 종류의 팬케이크가 (N + 1) / 2번 등장하는 순간부터는 그와 반대되는 종류의 팬케이크만 등장한다.

위의 조건을 추가하더라도 답은 변하지 않는다.

 

상한 팬케이크가 더 많다고 가정하자.

추가된 조건에 따라 상한 팬케이크의 개수는 정확히 (N + 1) / 2개이다.

맨 위에 있는 팬케이크와 상한 팬케이크(마지막 것 제외) 바로 밑에 있는 팬케이크는 p의 확률로 상한다.

따라서 상한 팬케이크가 더 많다면 (N + 1) / 2개의 팬케이크는 p의 확률로 상한다.

 

이번에는 상하지 않은 팬케이크가 더 많다고 가정하자.

추가된 조건에 따라 상하지 않은 팬케이크의 개수는 정확히 (N + 1) / 2개이다.

상하지 않은 팬케이크(마지막 것 제외) 바로 밑에 있는 팬케이크는 (1 - p)의 확률로 상한다.

따라서 상하지 않은 팬케이크가 더 많다면 (N - 1) / 2개의 팬케이크는 (1 - p)의 확률로 상한다.

 

잘 생각해보면 문제를 다음과 같이 변형할 수 있다.

  • (N + 1) / 2개의 팬케이크는 p의 확률로 상하고 (N - 1) / 2개의 팬케이크는 (1 - p)의 확률로 상한다.
  • 이때 상한 팬케이크가 더 많을 확률을 구하시오.

N개의 팬케이크 중 p의 확률로 상하는 것 하나를 제외한 (N - 1)개의 팬케이크만을 고려하자.

그리고 (N - 1)개의 팬케이크 중 상한 팬케이크와 상하지 않은 팬케이크의 개수가 동일하지 않다고 가정하자.

이때 개수의 차이는 2 이상이므로 나머지 한 개의 팬케이크의 종류와는 상관 없이 결과는 확정된다.

또한 (N - 1)개의 팬케이크 중 상한 팬케이크가 많을 확률과 상하지 않은 팬케이크가 많을 확률은 동일하다.

 

이번에는 (N - 1)개의 팬케이크 중 상한 팬케이크와 상하지 않은 팬케이크의 개수가 동일하다고 가정하자.

이 경우에는 나머지 한 개의 팬케이크의 종류에 따라 결과가 결정된다.

(N - 1)개의 팬케이크 중 두 종류의 개수가 동일할 확률을 k라고 하면 답은 다음과 같이 계산할 수 있다.

ans = (1 - k) / 2 + k * p;

 

k는 다음과 같이 구할 수 있다.

int n = (N - 1) / 2;

for i = 0; i <= n; ++i

    k += nCr(n, i)^2 * p^(2 * i) * (1 - p)^(2 * (n - i));