RCC Final round C - Eleventh Birthday

2018. 1. 6. 22:51알고리즘 문풀/Others

Lobelia, RickMcCoy 와 함께 연습한 대회다. (팀명 : Red Lung)

나는 C를 잡았는데, 결과는 1 solve로 초라했지만 대회가 끝나고 1시간 가량 더 잡아서 풀어냈다.

(애초에 QR을 치려고 했는데 내가 예약을 FR로 잘못한 걸 나중에서야 알았다...)


nn장의 서로 다른 카드에 숫자 a1,a2,ana_1, a_2, \cdots a_n이 적혀 있다. (1ai 1091 \le a_i \le 10^{9}) 이 카드들을 적당히 한 줄로 길게 이어붙여 만든 큰 숫자가 1111의 배수가 되는 경우의 수를 119×223+1119 \times 2^{23} + 1로 나눈 나머지를 구하여라. 


Tag : Combinatorics, Dynamic Programming, FFT, DP optimization with FFT



Step 0. Initialization


11의 배수판정법을 이용하면, aia_i에서 중요한 것은 1111로 나눈 나머지와 자릿수의 기우성 뿐임을 알 수 있다. 

전체 수의 나머지에 대해, 

어떤 수 xx의 오른쪽 (낮은 자리)에 있는 수들의 개수가 홀수인 수는 11x11-x만큼,

오른쪽의 수 개수가 짝수인 수는 xx만큼 기여할 것이다. (뭐라는거야...)


즉, 수를 이어붙이다가 자릿수가 홀수인 숫자가 나올 때마다, 나머지에 기여하는 "부호가 바뀐다".


따라서 aia_i들을 2개의 vector에다가 자릿수의 기우성에 따라 분류하여 넣는데, ai mod 11a_i \text{ mod } 11을 벡터에 넣는다.


Step 1. 홀수 자릿수인 수들의 순열 개수 구하기


애초에 이 문제는 가능한 '순열'의 수를 구하는 문제이기 때문에 재귀적인 DP로는 잘 풀리지 않는다. 

즉, 가능한 '조합'의 수를 구한 뒤 거기에 팩토리얼을 곱하는 유형일 가능성이 높다.


홀수 자릿수인 수들을 11로 나눈 나머지를 각각 o1,o2,obo_1, o_2, \cdots o_b라고 하자.

이들을 일렬로 순서대로 배열한 것을 오른쪽부터 oσ1,oσ2oσbo_{\sigma_{1}}, o_{\sigma_{2}} \cdots o_{\sigma_{b}}라 두면


oσ2ko_{\sigma_{2k}}는 부호가 바뀌어서, oσ2k1o_{\sigma_{2k-1}}은 부호가 그대로 들어갈 것이다.


즉, oio_{i}에 임의로 부호를 부여했을 때, 음수를 부여받은 숫자가 정확히 b2\lfloor \frac{b}{2} \rfloor개인 조합의 수에 b2!b2!\lfloor \frac{b}{2} \rfloor ! \cdot \lceil \frac{b}{2} \rceil ! 를 곱한 것이 홀수 자릿수 순열의 가능한 배열 개수가 된다.


조합의 수는 생성함수를 이용하여 구할 수 있는데, 먼저 y11=y0y^{11} = y^{0}인 변수 yy를 잡자.


여기서 yy는 복소수가 아니고, 그래서 y01y^{0} \neq 1이다.일종의 벡터 (a0,a1,a10)(a_{0}, a_{1}, \cdots a_{10})를 다항식의 form a0y0+a1y1+a10y10a_{0}y^{0} + a_{1}y^{1} + \cdots a_{10}y^{10}로 생각하기 위해 도입한 변수이다.


(a0,,a10)×(b0,,b10)=(c0,,c10)(a_{0}, \cdots, a_{10}) \times (b_{0}, \cdots, b_{10}) = (c_{0}, \cdots, c_{10})이라고 하면

ci=(i+j)k(mod 11)aibjc_{i} = \sum_{(i+j) \equiv k (\text{mod 11})} a_{i}b_{j}이다. 이는 다항식의 곱과 매우 흡사한 form임을 알 수 있고, 

이것이 yy의 직관적인 정의가 수학적으로 해괴하게 기술되는 이유이다.


각각의 oio_{i}(yoi+y(oi) mod 11x)(y^{o_{i}} + y^{(-o_{i})\text{ mod }11}x)로 대응시킨 1차 다항식 bb개를 모두 곱하는 것이다. 


이 과정은 FFT와 분할 정복을 이용하여 O(blg2b)O(b \lg ^{2} b)에 수행될 수 있으며, mod\text{mod}가 좋으니 정수 FFT를 사용할 수 있다. 

(원시근은 33)


bb차 다항식에서 x b2ytx^{\lfloor \frac{b}{2} \rfloor}y^{t}의 계수가, 

b2\lfloor \frac{b}{2} \rfloor개의 숫자에 음수를 부여해서 적당히 합쳤더니  mod 11\text{ mod 11}tt가 되는 조합의 수가 된다. 


이 벡터를 A1A_{1}이라고 하자.



Exception 1.1. b=0b = 0인 경우


이 경우는 모든 수의 자릿수가 짝수이므로 답은 00 또는 n!n!이다. ai mod 11a_{i} \text{ mod } 11을 다 더해서 0인지 체크해주면 된다. 


Exception 1.2. b=nb = n인 경우


이 경우는 짝수 자릿수인 수가 존재하지 않으므로, A1A_{1}에서 y0y^{0}의 계수가 답이 된다.

아래는 1bn11 \le b \le n-1인 경우를 생각한다.


Step 2. 짝수 자릿수인 수들의 순열 개수 구하기


이 경우는 조금 더 복잡하다. 


똑같은 풀이를 쓰는데 원하는 답은 다르다.

수학적인 상황으로는 홀수들을 '칸막이'로 생각하고,

음수를 부여받은 수들을 2k2k번째 칸막이와 2k+12k+1번째 칸막이 사이에,

양수를 부여받은 수들을 2k12k-1번째 칸막이와 2k2k번째 칸막이 사이에 배치하도록 하는 경우의 수이다.


우선 [xp][x^{p}]xpx^p의 계수 a(p,0)y0+a(p,10)y10a_{(p,0)}y^{0} + \cdots a_{(p,10)}y^{10}이라고 생각하자.

짝수 자릿수 수들을 11로 나눈 나머지를 e1,e2,ece_{1}, e_{2}, \cdots e_{c}라고 하자. (b+c=nb+c = n)


이번에도 똑같이 (yei+y(ei) mod 11x)(y^{e_{i}} + y^{(-e_{i})\text{ mod }11}x)를 다 곱한다.


동자순열(같은 것이 있는 순열)을 이용해서 머리를 잘 굴리면, 

k=0c[xk](k+b12)!b12!(ck+b2)!b2!\sum_{k=0}^{c} [x^k] \cdot \frac{(k+\lfloor \frac{b-1}{2} \rfloor)!}{\lfloor \frac{b-1}{2} \rfloor!}\cdot \frac{(c-k+\lfloor \frac{b}{2} \rfloor)!}{\lfloor \frac{b}{2} \rfloor!}


에서 yky^{k}의 계수가 짝수를 잘 배치했을 때 mod 11\text{mod 11}kk가 나오게 할 수 있는 순열의 수임을 알 수 있다.

따라서 위 식의 벡터를 A2A_{2}라고 하자.


Step 3. 답 구하기


A1× A2A_{1} \times A_{2}y0y^{0} 계수가 답이 된다. 저 곱하기는 당연히 외적이 아니라 위에 정의된 벡터곱이다. 풀이 끝!


Complexity : O(112nlg2n)O(11^{2} \cdot n lg^{2} n)




FFT를 사용하지 않는 O(11n2)O(11n^2) DP 풀이가 있는데, 나의 FFT 구현이 상당히 느림에도 불구하고 내 코드가 3배 이상 빠르다.