해설
이 풀이에서는 격자의 행과 열을 -indexed로 생각한다. 즉 행 번호는 이고, 열 번호는 이다.
먼저 에라토스테네스의 체로 이하의 소수를 모두 구한다. 각 에 대해 를 만족하는 두 소수 를 잡는다. 라고 하자. 구현에서는 에 가까운 소수부터 확인하면 빠르게 찾을 수 있다. 이 문제의 제한에서는 필요한 모든 에 대해 이러한 소수쌍을 찾을 수 있다.
핵심은 격자를 왼쪽 개의 열과 오른쪽 개의 열로 나누는 것이다. 우선 일반적인 경우, 즉 이고 인 경우를 보자. 처음에는 다음과 같이 배열한다.
왼쪽 블록은 를 사용하고, 오른쪽 블록은 를 사용하므로 전체적으로 의 순열이다.
왼쪽 블록에서 가로로 이웃한 두 수는 항상 차이가 난다. 따라서 모두 서로소이다. 세로로 이웃한 두 수는 정확히 차이가 난다. 두 수의 최대공약수가 보다 크다면 그 최대공약수는 의 약수여야 하므로, 실제로 문제가 되는 것은 두 수가 모두 의 배수인 열뿐이다. 왼쪽 블록에서는 마지막 열 에
가 세로로 놓이므로 이 열만 고치면 된다.
오른쪽 블록도 비슷하다. 오른쪽 블록에서 가로로 이웃한 두 수는 차이가 나므로 모두 서로소이다. 세로로 이웃한 두 수는 정확히 차이가 난다. 따라서 오른쪽 블록에서 문제가 될 수 있는 곳은 의 배수들이 한 열에 모이는 곳뿐이다.
이제 두 문제 열을 각각 고치자.
먼저 왼쪽 블록의 의 배수 열을 고친다. 원래 , , , 이다. 이 네 값을 다음과 같이 교환한다.
그러면 마지막 열은
가 된다. 인접한 두 수는 각각 , , , 이고, 는 홀수 소수이므로 모두 서로소이다.
이 교환으로 영향을 받은 다른 간선도 확인해야 한다. 는 과 사이에 놓이고, 아래에는 가 있다. 이므로 이웃한 수들과 모두 서로소이다. 도 마찬가지로 과 사이에 놓이고 아래에는 가 있으며, 이므로 문제가 생기지 않는다. 마지막 열에 들어간 역시 양옆 또는 위아래의 수들이 모두 홀수이거나 의 배수가 아닌 형태라서 서로소 조건을 만족한다.
이제 오른쪽 블록의 의 배수 열을 찾는다. 라 하자. 또한
라 하자. 그러면 오른쪽 블록의 번 열에 의 배수들이 모인다. 실제로 이고, 이 열의 값은 위에서부터
이다.
가 짝수이면 다음과 같이 고친다.
즉 의 홀수 번째 배수들을 맨 위 행의 작은 수들과 교환한다. 그러면 번 열은
꼴이 된다. 이웃한 두 수를 보면 작은 수와 의 배수가 번갈아 나오며, 작은 수들은 각각 이웃한 계수와 서로소이고 보다 작다. 따라서 이 열의 세로 인접쌍은 모두 서로소이다.
가 홀수이면 다음과 같이 고친다.
이 경우 번 열은
꼴이 된다. 역시 작은 수와 의 배수가 번갈아 나오므로 세로 인접쌍이 모두 서로소가 된다.
방금 옮긴 의 배수들은 맨 위 행의 작은 열에 놓인다. 그 양옆의 수들은 연속된 작은 수이고, 아래쪽 수는 꼴이다. 인 일반 경우에는 여기서도 공통 약수가 생기지 않는다. 을 따로 처리하는 이유가 바로 이 부분에서 작은 소수와 충돌할 수 있기 때문이다.
두 블록의 경계도 확인해야 한다. 경계에서 번 행의 두 값은
이다. 이 두 수가 공통 소인수를 가진다고 하자. 그 소인수가 라면 가 를 나누어야 하는데, 이고 이므로 불가능하다. 그 소인수가 의 약수라면 가능한 작은 소수는 뿐이다. 는 오른쪽 값의 홀짝성 때문에 불가능하고, 또는 가 문제가 되는 경우는 등의 작은 경우로 따로 처리한다. 따라서 일반 경우의 경계도 모두 좋다.
이제 예외를 처리한다.
만약 이 소수라면 로 두고, 큰 블록의 폭을 , 작은 블록의 폭을 로 둔다. 다음과 같이 배열한다.
그러면 번 열에
가 모인다. 여기서 과 를 교환하고, 과 를 교환하면 이 열은
가 되어 세로 인접쌍이 모두 서로소가 된다. 나머지 새로 생긴 인접쌍은 작은 수 또는 주변만 확인하면 되고, 모두 서로소이다.
인 경우에는 오른쪽 블록의 모양을 조금 다르게 잡는다. 처음 배열은 다음과 같다.
이 경우에도 가로 간선은 대부분 자동으로 좋고, 문제가 되는 곳은 의 배수들이 모이는 열들뿐이다. 구현에서는 와 작은 수 를 먼저 교환하여 오른쪽의 배수 열을 분리한다. 이후 에 따라 왼쪽 마지막 열을 고친다. 이면 일반 경우와 같이 를 위쪽 행으로 옮기고 를 마지막 열에 넣는다. 그렇지 않으면 를 위쪽 행으로 옮기고 를 마지막 열에 넣는다. 각 경우에서 영향을 받는 간선은 상수 개뿐이며, 직접 최대공약수를 확인하면 모두 서로소이다.
인 경우에는 위의 일반 보정에서 작은 소수와 충돌할 수 있으므로 별도의 작은 블록을 사용한다. 작은 블록은 각각 크기이며 를 담는다. 큰 폭 블록에는 부터 까지를 규칙적으로 배치한다. 작은 블록 내부와 경계는 미리 좋은 쌍만 생기도록 만들어 두고, 큰 블록에서 의 배수들이 모이는 열만 몇 번의 교환으로 보정한다. 이 방식은 에서만 필요하므로 배열을 코드에 그대로 넣어도 충분하다.
마지막으로 처럼 아주 작은 값은 공간이 좁아 일반식의 열 인덱스가 성립하지 않을 수 있다. 이런 경우는 직접 만든 배열을 출력한다. 각 배열의 크기는 매우 작으므로, 순열 조건과 모든 인접쌍의 서로소 여부를 직접 확인할 수 있다.
전체 알고리즘은 다음과 같다.
- 모든 입력을 읽고 를 구한다.
- 에라토스테네스의 체로 이하의 소수를 구한다.
- 각 에 대해 위의 경우 중 하나를 적용하여 격자를 만든다.
체의 시간 복잡도는 이다. 여기서 이다. 각 격자를 출력하는 데 걸리는 시간은 격자 크기에 비례하므로 전체 출력 시간은 이다. 메모리 사용량은 소수 배열과 한 테스트케이스의 격자를 저장하는 데 필요한 이다.
이 구성은 모든 인접쌍을 좋은 쌍으로 만들므로 모든 테스트케이스에서 을 달성한다. 따라서 가능한 최대 점수를 얻는다.
Solution written by GPT5.5