https://www.acmicpc.net/contest/view/849
2022 신촌지역 대학생 프로그래밍 대회 동아리 연합 여름 대회 (SUAPC 2022 Summer) Open Contest
www.acmicpc.net
6문제 풀었습니다. 생각보다 잘한 듯?
수학적인 사고력을 요하는 문제가 많네요.
A
, ,
double 이차원 배열 dp를 다음과 같이 정의합시다:
dp[l][k] = (최솟값이 0, 최댓값이 l인 구간에서 k를 찾는 기댓값)
경우의 수를 적당히 잘 나누어주어 기댓값을 저장합니다.
시간 복잡도는 O(24002 + N)입니다.
B
그냥 구현
C
N≤106이라는 건 (아마) O(N2) 풀이가 안 된다는 거고, 딱히 정렬을 하는 방식을 사용할 거 같지 않으므로 O(NlogN) 풀이도 아닐 겁니다. 그러므로, O(N) 시간 복잡도를 갖는, 적당한 방법을 직관적으로 찾기를 요구하는 문제인 듯합니다.
일단 N이 적혀있는 카드는 가장 먼저 뽑아야 합니다: 안 그러면 N을 적혀있는 카드를 뽑는 사람은 패스를 2번 이상 받게 됩니다. 이후 생각이 잘 안 나서, N≤12에 대해서 브루트포스로 찍어보니, N이 짝수일 때만 정답이 구해지더라고요. 그래서 일단 짝수일때만 가능하다고 생각하고 풀었습니다. 풀고나서 생각해보니, N이 3 이상의 홀수일 때
∑N−1n=1n=N(N−1)2≡0(modN) 이기에, 처음 시작하는 사람은 무조건 2번 이상 패스를 받습니다.
N이 짝수일 때 처음 시작점을 1이라고 한다면, 2→N−1→3→N−2→... 에게 패스를 하면 된다는 사실을 관찰할 수 있습니다. '직관적인 관찰'이라 어떻게 생각했는지 모르겠네요.
그리고 N이 홀수라도 N=1일 때는 됩니다. 이 경우만 예외처리해주면 됩니다.
F
일반성을 잃지 않고, A1,...AN을 A1<A2<...<AN로 정합시다.
차의 개수의 최댓값
Ai=2i−1로 설정하면 차이의 개수는 N(N−1)/2개로 최대입니다. 229<10억이라 가능합니다.
차의 개수의 최솟값
|A2−A1|<|A3−A1|<...<|AN−A1| 이므로, 차의 개수의 최솟값은 최소 N−1개입니다.
그리고 간단히 Ai=i로 결정하면 차의 개수가 N−1개가 됩니다.
I
좀 디버깅이 끔찍한 case-work 문제입니다. 일단, 케이스를 좀 편하게 나누기 위해 row, col별로 씨앗의 개수를 세주고, 씨앗이 있는 row, col의 수, 그리고 씨앗이 뿌려져있는 칸의 수를 세줍니다.
1) 만약 씨앗이 뿌려져있는 칸의 수가 2K개라면 겹쳐서 씨앗을 뿌리지 않았으므로 −1 출력
2) 1)이 아니라면: 2K - '씨앗이 뿌려져있는 칸의 수'개만큼 씨앗을 겹쳐뿌렸습니다. case를 나누어,
I) 씨앗이 있는 row의 수가 1인 경우: 두 명 모두 한 줄에 겹쳐 뿌렸다는 뜻입니다. 몇 개가 겹쳤는지 '씨앗이 뿌려져있는 칸의 수'를 통해 구하고, '겹칠 수밖에 없는' 위치의 씨앗의 좌표를 저장해 출력합니다.
II) 씨앗이 있는 col의 수가 1인 경우: I)이랑 row/col 바꿔서 풀면 됩니다.
III) 그외: 한 명은 세로로, 한 명은 가로로 씨앗을 뿌렸습니다. 이 경우 최대 1곳에서만 만나며, 위 정보를 통해 빠르게 구해줍니다.
K=1일 때의 예외 처리를 해줘야할 거 같지만, 2) - I)에서 잘 처리가 됩니다.
그와 별개로 실수하기 참 쉬운 문제입니다. 저는 한 곳에서만 N,M을 바꿔썼다가 디버깅이 엄청 오래 걸렸습니다.
M
주제별로 뽑을 수 있는 경우의 수를 Xi라고 하면, 정답 X=∏Ni=1Xi 이고,
Xi=∑Nn=1(Ain)(Bin) where N=min(Ai,Bi) 입니다.
그리고
Xi=∑Nn=1(Ain)(Bin)=(Ai+BiN)−1
이 됩니다. 이는
(1+x)Ai+Bi=(1+x)Ai(1+x)Bi의 N차항의 계수를 비교하면 알 수 있습니다. 찾아보니 Vandermonde's Identity라고 합니다. 수학 전공한거 맞나? ㅋㅋ... 잘 모르겠어서 구글에 대충 쳐서 구했습니다.
그리고, 오일러 소정리를 이용하면, 임의의 소수 p에 대해,
(Ai+BiAi)≡(Ai+Bi)!Ai!Bi!≡(Ai+Bi)!(Ai)!p−2(Bi)!p−2(modp)
가 성립합니다. 본 문제에서 p=109+7입니다.
1! 부터 (A+B)!을 미리 구하고, 분할정복을 이용하여 p제곱을 계산하기에, 각 쿼리를 O(logp)에 처리할 수 있습니다. 시작 복잡도는 O((A+B)+Nlogp).
글 정리하겠다고 학부 시절에는 쳐다도 안 봤던 LaTex 문법을 다 써보네요ㅋㅋ
생각보다 쓰는 방법이 간단해서, 시간 복잡도 'O(N)' 정도의 표현에 대해서는 계속 써야겠습니다.