이제 문제를 해결할 수 있다. 기본적으로 a에 대한 조건이 없다면, 이는 n/T, m/T로 가능한 값이 적다는 점을 이용해서 구간을 나누고, 각 구간에 대한 f(T)=∑g|T(T/g)⋅μ(T/g)의 부분합을 미리 전처리하여 해결할 수 있다. 이러면 쿼리 당 루트 시간에 문제를 풀 수 있다.
문제는 a에 대한 조건이다. 이를 위해서, 쿼리를 오프라인으로 해결하자. 쿼리를 a 기준으로 정렬한다.
이제 다시 f(T)=∑g|T,g≤a(T/g)⋅μ(T/g)라고 정의하자. 이제 잘 생각해보자.
a가 증가하면 a의 배수인 T에 대하여 f(T)의 값이 변한다. 조화수열을 생각하면 변화 횟수는 많지 않다.
맨 처음의 식으로 돌아가서, 우리가 계산해야 하는 것은 결국 특정 구간에 속한 T에 대한 f(T)의 값들의 합이다.
그러니 결국 점 업데이트 + 구간 쿼리 문제가 되고, 이는 단순한 세그먼트 트리를 이용하여 해결할 수 있다.
수학 대회 기출에서 가져온 문제지만 어쨌든 좋은 문제는 맞는 것 같다. 다음 관찰을 순서대로 해야한다. 증명은 생략.
1. S(x)는 단사함수다. 이 문제는 Putnam 2013 A2 문제이기도 하다. 여기까지는 쉽다.
2. S(x)는 사실 자연수에서 소수가 아닌 자연수로 (즉 1 포함) 가는 전단사함수이다. 이건 조금 까다롭다.
3. 2를 증명하는데 중요한 관찰은 다음 사실이다. x가 소수가 아닌 자연수라 하자. x=t1>t2>⋯>tk가 있어 t1⋅t2⋯tk가 제곱수가 되도록 하는 tk의 최댓값을 T(x)라고 하자. 이 값이 잘 정의됨을 보이는 것은 어렵지 않다. 이때, S(T(x))=x가 성립함을 보일 수 있다.
결국 문제는 y가 소수인지 판별하고, 아니면 T(y)를 구하는 문제가 된다. 이제 이 문제와 비슷해진다.
이 문제의 근본적인 해결 방법은 소인수의 지수들의 홀짝성을 F2의 벡터로 보는 것이다.
답에 대해서 이분탐색을 하자. 그러면 m,m+1,⋯,y−1의 이진 벡터를 선형결합하여 y의 벡터를 만들 수 있는지 판별하는 문제가 된다.
이를 위해서는 가우스 소거를 할 필요가 있는데, 여기서 중요한 관찰이 하나 더 필요하다.
어쨌든 1000 이상의 소수를 2개 이상 가질 수는 없다는 것이다. 그러니 모든 벡터를 1000 이하 소수 200개쯤에 대한 벡터와 "내가 특별히 가지고 있는 1000 이상 소수 하나 (없을 수도 있음)"으로 보는 것이 좋다. 이 관찰로 시간복잡도를 획기적으로 줄일 수 있다.
이 점을 고려하면 가우스 소거를 매우 효율적으로 할 수 있으며, std::bitset으로 최적화를 하면 문제가 해결된다.
문제에서 등장하는 수열은 OEIS에도 있는 수열로, 레퍼런스를 보면 관련 문제가 수학 잡지에도 등장한 것으로 보인다.
정말 오랜만에 rated round를 쳤다. 앳코더나 코포나 레이팅이 변하는 라운드는 2018년 초 이후로 처음이다.
문제가 조금 별로였고, 그런 라운드만 잘 치는 나는 덕분에 나쁘지 않게 대회를 쳤다.
C, A를 빠르게 해결한 것에 비해 B, D가 느렸는데, 풀이를 빠르게 찾았지만 구현에서 뇌절을 한 것이 컸다.
15분을 남겼을 때 E small을 읽고 풀려고 했는데, 손을 놓아버린 것도 아깝다. E small까지는 풀었어야 했다고 본다. ㄲㅂ
A : 어쨌든 중요한 것의 2, 5의 개수이므로 이것만 잘 따지면 된다. cnt[a][b]를 2가 a개, 5가 b개인 수의 개수라고 하면 된다. 중복 카운팅을 조심해서 처리하자. 파싱하는 게 가장 어렵고 귀찮은 개노잼 문제다. 예전에는 무슨 가우스 소거를 A에 내더니 뭔...
B : S가 T로 갈 수 있는 필요충분조건은, (단, S≠T 가정) 우선 당연히 |S|>|T|이고, T의 길이 |T|−1 suffix가 S의 길이 |T|−1 suffix와 같아야 하며, T의 첫 글자가 S의 첫 |S|−|T|+1개의 글자에서 존재해야 한다. 이를 기반으로, 해시를 해서 문제를 해결할 수 있다. suffix의 hash와 대응되는 prefix에 존재하는 알파벳에 대하여 카운트를 늘려주면, 이를 통해서 답을 구할 수 있다. 이를 단순하게 하면 TLE가 발생하고, 실제로 관심이 있는 해시 값인 각 문자열 Si의 길이 |Si|−1 suffix의 hash에 대해서만 계산하면 통과된다. 모든 문자열을 뒤집고 Trie를 써도 된다.
C : 곱셈을 덧셈으로 바꾸면 FFT가 가능할 것 같다. 이를 가능하게 하는 도구는 로그다. 근데 정수론 문제니까 그냥 로그말고 이산로그를 쓰면 된다. P의 값이 작으므로, 문제 없다. 원시근은 2를 쓰면 된다. 걍 개노잼...
D : 개인적으로는 재밌게 풀었던 문제. 더 좋은 풀이가 많은 것 같으니 한 번 확인할 필요는 있겠다. 일단 DP[i]를 i에서 위로 올라가서 1까지 곱한 값이라고 하고, IV[i]를 DP[i]의 잉여역수라고 하자. 그러면 우리가 구해야 하는 값은 각 1≤i<j≤L에 대하여 DP[L+i−1]⋅DP[L+P[i]−1]⋅DP[L+j−1]⋅DP[L+P[j]−1]DP[LCA(L+i−1,L+j−1)]⋅DP[LCA(L+i−1,L+j−1)/2]⋅DP[LCA(L+P[i]−1,L+P[j]−1)]⋅DP[LCA(L+P[i]−1,L+P[j]−1)/2]를 합한 값이다. 식이 어마어마하게 더러운데, 천천히 생각해보자. 우선 i를 고정하고, LCA(L+i−1,L+j−1)을 고정해보자. 이렇게 되면 가능한 j의 영역이 구간을 이루게 된다. 우리가 여기서 걱정해야 하는 것은 DP[L+j−1]⋅DP[L+P[j]−1]의 값과, LCA(L+P[i]−1,L+P[j]−1)의 위치이다. 그런데 LCA(L+P[i]−1,L+P[j]−1)의 위치를 고정하면, 원하는 P[j]의 위치도 일종의 구간이 된다. 그러니 결국 "j의 구간, P[j]의 구간이 주어졌을 때 그 안에서 DP[L+j−1]⋅DP[L+P[j]−1]의 합을 구하라"는 문제가 된다. 이는 Persistent Segment Tree로 할 수 있다. 이러면 2HH3 풀이가 완성되는데, 이를 최적화할 수 있다. 아까도 말했지만 P[j]의 구간에 따라서 LCA의 위치가 달라지는데, 이 구간이 이진트리 특성상 어마어마하게 깔끔하다. 그래서 사실 PST를 위에서부터 내려가는 것으로 P[j]의 구간에 따른 각 쿼리를 로그 시간에 전부 해결할 수 있다. 그래서 H 하나가 빠지고 2HH2 풀이가 나온다. 끝. 나의 경우 PST의 초기 크기를 단순히 217로 잡지 않고, 2H−1로 정확하게 잡는 것이 중요했다. 이걸 놓쳐서 디버깅이 힘들었다. 다이아 5 ~ 다이아 4 정도 되는 문제 같은데 그래도 시간 안에 해결했다는 점은 긍정적이다.
E : 재밌어 보이는 문제인데, 먼 미래에 업솔빙을 하는 것으로...
출처는 잘 모르는 문제.
입력으로는 a,N이 주어지며, 이때 1≤x≤y≤N, gcd(x,y)=1, x|y2+a, y|x2+a를 만족하는 (x,y)의 개수를 구해야 한다.
1≤a≤105, 1≤N≤1018. 테케는 총 106개가 있으며, 전부 5초 내에 해결해야 한다. 메모리 제한은 256MB.
우선 x|y2+a, y|x2+a란 조건은 gcd(x,y)=1과 함께 생각했을 때, xy|(x2+y2+a)라는 것으로 바꿀 수 있다.
이제 이는 수학 올림피아드 계에서는 유명한 비에타 점핑 테크닉을 쓸 수 있는 문제가 된다.
x2+y2+a=kxy가 성립한다고 하고, 이를 만족하는 x+y가 가장 작은 순서쌍 (u,v)를 생각하자. WLOG, u≥v다.
그런데 생각해보면 (u,v)가 조건을 만족하면, (kv−u,v) 역시 조건을 만족하고, 이때 kv−u=(v2+a)/u다.
그런데 최소성의 조건에서 (v2+a)/u≥u가 성립하고, v2+a≥u2을 얻는다.
즉, x2+y2+a=kxy를 만족함과 동시에 y2≤x2≤y2+a를 만족하는 (x,y)를 전부 구하면, 이 해들이 "기본해"가 된다.
이는 a≤105이므로 약수를 전처리하고 lower bound 등을 잘 활용하는 것으로 후보군을 줄여서 잘 계산할 수 있다.
이제 이 "기본해"를 가지고 각 a에 대한 해를 전부 생성하고, std::vector에 저장하자.
쿼리에 대답하는 것은 간단한 lower bound로 해결할 수 있다. 시간/메모리가 모두 빡빡해서 조심히 구현해야 한다.
그러니 ⌊n/T⌋, ⌊m/T⌋가 가질 수 있는 구간의 개수가 적다는 점을 이용하여, 각 구간에 대한 ϕ 함수의 합을 차례대로 구해주면 충분하다. 이는 f(x)=ϕ(x), g(x)=1, (f∗g)(x)=x를 이용한 xudyh's sieve로 가능하다. 다른 식들도 마찬가지 방법으로 정리가 가능하고, 결국 xϕ(x), x2ϕ(x)의 부분합을 구하는 문제로 환원된다. 이들 역시 마찬가지 방법으로 xudyh's sieve를 사용하여 해결할 수 있다. 나름 전형적인 듯.
우선 각 m에 대하여 답을 구한 다음 부분합을 취하면 되는 형태다. 아예 다 구하고 출력만 하라는 뜻이다.
또한, 각 m에 대한 답은 자명하게 (중국인의 나머지 정리) multiplicative 하다. 그러니 prime power에 대해서만 해결하자.
이 prime power가 홀수인 경우, 문제는 어렵지 않다. (x+y)2≡a+2b, (x−y)2≡a−2b이므로 a+2b, a−2b는 이차잉여이고, 역으로 이 조건만 만족하면 조건을 만족하는 x,y도 있기 때문이다. 그러므로 답은 해당 prime power에 대한 이차잉여의 개수를 제곱한 것이다.
prime power가 2의 거듭제곱인 경우 상황은 조금 복잡하다. 작은 거듭제곱의 경우 brute force로 답을 구할 수 있고, 믿음의 Berlekamp-Massey 알고리즘으로 답을 전처리할 수 있다. 아예 하드코딩해도 된다. 이제 이를 합쳐서 답을 구하는 것은 쉽다. 벌레캠프 각을 너무 늦게 봐서 해결이 오래 걸렸다.
Hackenbush에 대한 간단한 튜토리얼을 쓰려고 하는데, 내가 이걸 잘 아는 거는 아니라서 (...) 증명 및 깊이 있는 이론들은 (ordinal 등장 등) 대부분 생략하고 알고리즘 문제 풀이에 써먹을 수 있는 부분들만 간략히 추려서 쓰려고 한다. 자세한 증명은 구글에 검색하면 자료가 매우 많으니 찾아보는 것을 추천한다 :)
Hackenbush 게임은 두 명이서 하는 게임으로, 여러 가지 버전들이 존재하지만 근본적인 목표는 똑같다.
위 그림과 같이, 일종의 그래프가 지면에 연결되어 있는 상태에서 게임이 시작된다.
각 플레이어는 순서대로 간선 하나를 제거하는데, 이때 더 이상 지면에 연결되지 않게 되는 간선들은 모두 삭제된다.
일반적인 룰에서는 (normal play convention) 간선을 제거하지 못하게 되는 플레이어가 패배한다.
간선이 무한히 많은 경우도 존재하지만, 여기에서는 유한한 개수의 간선이 존재한다고 가정한다.
그러면 게임이 확실하게 Sprague-Grundy 방식으로 분석이 가능해진다. 이를 위해서 다음 Principle을 활용한다.
Colon Principle: 그래프 G,H,K가 있다. H,K의 Grundy 값이 같다면, G의 정점 x에 H를 붙인 그래프와 x에 K를 붙인 그래프의 Grundy 값이 동일하다. 그러므로, 한 점에서 '가지치기'가 되는 그래프가 있다면, 각 가지들의 Grundy 값의 XOR에 해당하는 길이의 일직선을 붙이는 것으로 가지들을 대체할 수 있다. 이를 통해서 트리 그래프에 대한 분석을 끝낼 수 있다. DFS를 돌면서 서브트리의 Grundy 값을 순서대로 계산하면 된다.
Fusion Principle: 두 정점이 한 사이클 위에 놓인다면 (지면을 하나의 노드로 본다) 두 점을 하나로 합쳐도 Grundy 값이 변하지 않는다.
이때 두 점을 잇는 간선들은 self-loop가 된다. 이 성질을 이용하면, 임의의 그래프에 대한 Grundy 값을 구할 수 있다.
실제로 이를 활용하는 문제는 본 적이 없지만, 아무튼 이를 이용하면 기본 Hackenbush에 대한 분석은 끝이다.
연습문제: Project Euler 400 Fibonacci Tree Game, BOJ 13893 Dictionary Game
두 문제 다 Hackenbush에 대한 이해와 적당한 구현을 (DP/Trie 등) 통해서 해결할 수 있다. Fusion Principle은 필요 없다.
Blue-Red Hackenbush는 조금 색다르다. 이제는 각 간선마다 누가 자를 수 있는지가 둘 중 한 명으로 정해져 있다.
각 플레이어를 Blue/Red라고 하고, Blue 간선은 Blue만, Red 간선은 Red만 자를 수 있도록 한다.
이때는 각 연결 컴포넌트 G에 대하여, 그 컴포넌트의 값 v(G)를 다음과 같이 재귀적으로 정할 수 있다.
아무것도 없는 그래프의 경우 값은 자명하게 0이다. G가 nonempty라고 가정하자.
b를 Blue가 만들 수 있는 상태 G′ 중 v(G′)의 최댓값이라 하자. 불가능한 경우 −∞.
r을 Red가 만들 수 있는 상태 G′ 중 v(G′)의 최솟값이라 하자. 불가능한 경우 ∞.
만약 b<n<r인 정수 n이 존재한다면, v(G)는 해당하는 정수 n 중 0에 가장 가까운 것이다.
그렇지 않다면, v(G)는 b<x<r을 만족하는 유리수 x 중 분모가 2의 거듭제곱인 것 중에서 분모가 최소인 것이다.
또한, 독립적인 그래프 G,H에 대하여 v(G+H)=v(G)+v(H)가 성립한다.
만약 v(G)>0이라면 Blue가 승리, v(G)<0이라면 Red가 승리, v(G)=0이라면 후공 승리가 된다.
이 게임에 대한 직관적인 이해를 원한다면 stonejjun의 블로그를 찾아보는 것을 추천한다.
이걸 모두 짬뽕한 Blue-Red-Green Hackenbush도 있는데, 이건 진짜 무서워 보인다 ㅋㅋ
연습문제: BOJ 13409 Black and White Boxes, BOJ 18945 조직된 ㄱ폭탄 게임
13409는 Hackenbush 값을 계산한 다음 Meet In The Middle을 하면 간단하게 해결할 수 있다.
18945는 몇 가지 관찰을 한 다음, 문제를 Blue-Red Hackenbush로 변형하여 해결할 수 있다.
이 글의 진짜 목적 중 하나가 18945의 풀이를 적는 것이니, 여기에 소개한다.
각 게임을 독립적인 Blue-Red Hackenbush 게임으로 보고, 그에 대응하는 값을 계산하자.
그러면 남은 쿼리들은 전부 단순한 point update range query 문제가 되므로, 펜윅/세그먼트 트리를 이용하여 계산할 수 있다.
그러니 문제 난이도의 99%는 각 게임을 Blue-Red Hackenbush 게임으로 변환하는 것에서 나온다.
일반성을 잃지 않고 A 폭탄이 하나 존재한다고 가정하자. 그러면 B 폭탄을 3가지 종류로 나눌 수 있다.
종류 1: A 폭탄이 터지면 강제로 제거되는 B 폭탄들
종류 2: 터뜨리면 A 폭탄이 터지게 되는 B 폭탄들
종류 3: A 폭탄과 서로 interact 할 수 없는 (즉, 하나가 터져도 다른 하나가 터지지 않음) B 폭탄들
이 게임판에서 A가 할 수 있는 행동은 단순히 A 폭탄 하나를 터뜨리는 것 뿐이다.
B가 할 수 있는 행동은 다양한데, 실제로 고려해야 하는 것은 몇 가지 없음을 알 수 있다.
우선 종류 3의 폭탄과 같은 경우에는 애초에 A 폭탄이 터지던 말던 상관이 없기 때문에, 최대한 마지막에 터뜨리는 것이 좋다.
물론, 자신이 터뜨린 폭탄에 의해 자신의 다른 B 폭탄이 쓸데없이 터지지 않도록 순서를 잘 정해서 터뜨려야 한다.
종류 2의 폭탄은 터뜨린다면 강제적으로 종류 1의 폭탄 중 남아있는 것은 무조건 터지게되며, 경우에 따라서 종류 3의 폭탄 역시 터질 수 있다.
그러니 A 폭탄을 제거할 목적으로 종류 2의 폭탄을 터뜨린다면 종류 3의 폭탄을 최소한으로 터뜨리도록 하는 폭탄을 하나 찾아서 그걸 터뜨려야 한다.
종류 1의 폭탄은 터뜨린다면, 다른 종류 1의 폭탄을 터뜨리지 않도록 행/열 순서를 잘 잡아서 순서대로 터뜨려야 한다.
이를 종합하면, 다음과 같은 Blue-Red Hackenbush 그림을 그릴 수 있다. A가 Red, B가 Blue라고 하자.
그래프가 특수한 형태를 가진다. 그러니, 이와 같은 경우에서 Hackenbush 값은 아예 closed form 식을 통해서도 구할 수 있다.
나는 DP를 이용하여 위에서 설명한 규칙을 직접 적용하는 방식으로 구현했다. 분수를 다루기 귀찮으니, 적당히 큰 2의 거듭제곱을 곱해줘서 다루면 편하다.
DP 식 자체는 간단하고, cost 함수인 n∑i=1(1+⌊log2i⌋) 및 실제로 구하고자 하는 값이 n에 대하여 볼록함은 쉽게 추측할 수 있고 증명도 수학적 귀납법으로 어렵지 않다. 그런데 n 범위가 1015로 매우 높고, 아마도 출제자가 답이 long long int 범위를 넘어가도록 문제를 만들지는 않았을 것이므로, 답이 증가하는 폭이 꽤 작을 것이라고 유추할 수 있다. 즉, 실제로 답의 기울기가 변화하는 점이 적을 것이라고 생각하는 것이다. 이제 기울기가 변화하는 점을 이분탐색/삼분탐색을 잘 섞어서 찾아나가면 된다. 이 아이디어만 잡으면 그 뒤는 어렵지 않은데 아이디어 잡기가 굉장히 어려운 문제인 것 같다.
정말 재밌게 푼 문제다. 깔끔하고 아이디어도 좋은 문제. 이 문제는 풀이만 써놓기엔 좀 애매해서 생각 과정도 적는다.
Part 1: 기본적인 관찰
결국 문제의 statement는 각 문제에 대한 아이디어는 시작 후 0,1,⋯t−1분 후 중 한 시각에 얻어지고, 각 확률이 1/t로 동일하며 이는 문제마다 독립적이라는 것이다. 이는 p⋅tn이 정수인 이유가 되고, 동시에 이 문제가 확률 문제가 아닌 카운팅 문제임을 시사한다.
Part 2: n이 작은 경우 직접 계산
우선 x1+x2+⋯+xn>t인 경우에 답이 0임은 자명하다. n=1인 경우 답이 t−x1+1임도 자명하다.
n=2인 경우를 열심히 계산했고, (이 계산은 어렵지 않다) (t−x1−x2+1)(t−x2+2)를 얻었다.
이는 문제의 답이 생각보다 간단한 형태를 가질 거라는 믿음을 나한테 줬다. 도움이 꽤 된듯.
Part 3: n=t, x1=x2=⋯=xn=1인 경우
예제 2가 이런 경우고, n=5였다. 이때 예제 답이 1296이었는데, 이건 64다.
뭔가 느낌이 쎄해서 n=2, n=3인 경우를 손으로 계산했는데, 각각 31, 42가 나왔다.
여기서 나는 답이 (n+1)n−1이라고 확신했고, 증명을 고민하기 시작했다. 근데 생각해보니 아는 내용이었다.
이 경우에는 중간에 문제가 바뀌는 걸 고민할 필요가 없다. 시간 안에 아이디어가 공급되는 것만이 중요하다.
각 문제의 아이디어가 나오는 시간을 t0,t1,⋯,tn−1이라 하면, 정렬 후 i번째 값이 i 이하이면 된다. (0-index)
예전에 O(n2) 논문이 있길래 이걸 구현해보려다가 뇌절을 심하게 하고 멘탈 나가서 놓은 문제였다.
풀이는 이 블로그에서도 언급한 EGZ 정리의 증명을 이용한다. 우선 합성수인 경우, 문제를 쪼개서 합칠 수 있다.
n=ab라 쓰자. 단, a,b>1. 2ab−1개의 수가 있을 때, a에 대한 문제를 여러 번 풀어 합이 a의 배수가 되도록 묶인 2b−1개의 그룹으로 만들 수 있다. 2a−1개의 수를 가지고 합이 a의 배수인 그룹을 하나 만들고, 사용되지 않은 수를 다시 써먹는 것을 반복하면 된다.
이제 2b−1개의 그룹을 가지고 단순히 b에 대한 문제를 풀면 된다. 이는 구현이 귀찮지만 어렵지는 않다.
여기서는 Cauchy-Davenport 정리를 사용한다. 주어진 수를 정렬하여, 0≤a1≤a2≤⋯≤a2p−1<p라 하자.
이제 ai=ai+p−1인 i가 있다면 i부터 i+p−1을 전부 택하면 합이 자명하게 p의 배수가 된다.
그렇지 않다면, 주어진 수를 {a1},{a2,ap+1},{a3,ap+2},⋯,{ap,a2p−1}로 분할하자.
첫 집합을 제외하면, 각 집합의 크기는 (중복 원소가 없으므로) 2가 된다. 이제 순서대로 Cauchy-Davenport를 쓰면, 각 집합에서 한 원소를 골라서 p의 배수를 만들 수 있음을 얻는다. 이를 knapsack과 같은 방법으로 처리하면 끝. 참 멋진 문제라고 생각한다.
Online FFT 테크닉을 이용하여 해결할 수 있는 문제다. 여기서는 이 테크닉을 간단하게 설명한다.
배열 a가 주어져 있고, 이때 ci=∑ij=0ajai−j를 구하고 싶다고 하면, 단순히 FFT를 적용하면 된다.
그런데 만약 ai가 ci와 관련된 값이라던가, 배열 a가 바로 주어지지 않고 online으로 주어지면 어떻게 될까?
이때는 단순한 FFT로는 해결하기가 어려우나, 분할 정복을 추가하여 해결할 수 있다.
여기서는 설명의 간단함을 위해 a0=0이라고 가정하고, ci=∑ij=1ajai−j를 계산해야 ai를 얻을 수 있는 구조라고 생각하자.
카탈란 수의 점화식을 순수하게 FFT 하나로 계산한다고 생각하면 편할 것 같다.
[L,R)에 대해서, 문제를 해결하는 재귀함수 f([L,R))을 생각해보자. 이때 기본 가정은, 각 L≤x<R에 대하여 a0,a1,⋯,aL−1의 값을 이미 알고 있으며 각 L≤t<R에 대해 pL(x)2=(∑L−1i=0aixi)2의 xt 계수를 저장해놨다고 가정한다.
먼저 M=(L+R)/2를 잡고, f([L,M))을 호출하자. 이제 f([M,R))을 호출할 준비를 해보자.
현재 저장해놓은 M≤t<R에 대한 pL(x)2의 xt의 계수들을 pM(x)2의 xt의 계수들로 바꿔야 한다.
이는 결국 (pM(x)−pL(x))(pM(x)+pL(x))의 xt 계수를 구하는 것과 마찬가지다.
pM(x)−pL(x)는 L차 이상 M차 미만 단항식들로 구성되어 있다. 또한, 구해야 하는 계수는 최대 R−1차이다.
그러니, pM(x)+pL(x)의 R−1−L차 이하 단항식들만 고려해도 충분하다. 그러니 곱해야 하는 두 다항식의 실질적인 차수는 R−L에 비례한다. 개인적으로는 L=0인 경우를 따로 고려하는 것이 마음이 편하다. 어쨌든 정복 단계라고 볼 수 있는 스텝이 FFT로 O((R−L)log(R−L))에 된다.
그러니 총 시간복잡도는 O(Nlog2N)이 된다. 로그 하나는 순수한 분할정복에서 나오므로 꽤 빠르다.
감동적으로 좋은 문제. 우선 많은 XOR 문제가 그렇듯이, 비트로 쪼갤 생각을 해야 한다.
m1≥m2≥⋯≥mn이라고 하고, 2t≤m1<2t+1이 성립한다고 하자. 특히, m1부터 mk가 2t 이상이고, 나머지는 2t 미만이라고 하자.
우리는 기본적으로 2t 비트를 짝수개 뽑아야 한다. 1≤i≤k인 i에 대하여, i번째 변수에서 2t를 뽑는다면 그 변수의 범위는 2t에서 mi가 된다.
그렇지 않는다면, i번째 변수는 0부터 2t−1까지를 아무거나 뽑아줄 수 있다. 이는 굉장히 강력한 조건이며 이 문제의 핵심이다.
만약 1≤i≤k 중 2t를 뽑지 않는 i가 있다면, 그 i를 제외하고 다른 변수들이 2t 비트만 제대로 맞춰주면 전체 XOR이 0이도록 i번째 변수의 값을 유일하게 결정할 수 있기 때문이다. 이를 생각하면, "처음으로 2t를 뽑지 않는 index i"를 기준으로 카운팅을 해서 답을 얻을 수 있다.
만약 모든 1≤i≤k에 대하여 2t를 뽑는다면, k는 짝수여야 하며, 2t 비트를 모두 제거했다고 가정하고 더 작은 문제를 해결하면 된다.