문제
문제 해결
어떻게 해결해야 할 지 도저히 감이 안와서 상위 랭커분들의 코드를 참고했다. (들어가서 보고싶은 해당 문제 번호를 더블 클릭하면 된다.)
https://codeforces.com/contest/1631/standings
풀이의 핵심은 n이 2의 거듭제곱 꼴로 주어진다는 것에 있다. n이 2의 m제곱이라면 n-1의 이진수는 첫 번째부터 m번째까지의 모든 비트가 1이다. 예를 들어 n = 8(2의 3제곱)은 0부터 7까지므로 이를 이진수로 나열하면 아래와 같다. 편의상 하위 8비트까지만 나타냈다.
0 : 0000 0000
1 : 0000 0001
2 : 0000 0010
3 : 0000 0011
4 : 0000 0100
5 : 0000 0101
6 : 0000 0110
7 : 0000 0111
여기서 알고 있어야 하는 중요한 개념은 다음과 같다.
① (0 & (n-1)) + (1 & (n-2)) + ... + ((n/2-1) & (n/2)) = 0
② 0 ≤ i ≤ n-1일 때, i & (n-1) = i가 항상 성립한다. n은 2의 거듭제곱이기 때문에 n-1과 &연산을 하면 항상 자기 자신이 나온다.
①의 내용을 좀 더 살펴보자.
0의 m번째 비트까지 ^(XOR)연산을 한 결과는 n-1 → 0의 짝은 n-1,
1의 m번째 비트까지 ^(XOR)연산을 한 결과는 n-2 → 1의 짝은 n-2,
...
으로 설정할 수 있고, 두 수가 짝이 될 수 있는 이유는 &연산의 결과가 모두 0이기 때문이다. 또한 모든 수는 항상 자신의 짝이 존재하며 짝의 총 개수는 n/2개다.
m번째 비트까지 모두 1인 수는 n-1이므로, i와 그의 짝은 ((n-1)^i)로 구할 수 있다.
문제는 크게 세 가지 경우로 나눌 수 있다.
(1) k가 0인 경우
(2) k가 n-1인 경우
(3) 1 ≤ k ≤ n-2인 경우
(1) k가 0인 경우 :
(0 & (n-1)) + (1 & (n-2)) + ... + ((n/2-1) & (n/2)) = 0이므로 각각의 짝을 출력하면 된다.
(2) k가 n-1인 경우 :
n - 1 = n - 2 + 1 로 볼 수 있는데, n - 2는 ((n-1) & (n-2))로 구할 수 있다.
그렇다면 구해야 될 남은수는 1이고, 원래 각각 n-1, n-2의 짝이었던 0, 1은 짝이 사라진 상태가 된다.
위 두 상황을 한 번에 해결하는 방법이 0, 1의 잃어버린 짝을 각각 n/2, n/2-1로 설정하는 것이다. n/2와 n/2-1은 항상 서로 짝이므로 이를 0과 1에 매칭시키는 것이다.또한 n/2-1은 항상 홀수이기 때문에 n/2-1의 최하위 비트는 1이기 때문에 이를 1과 &연산하면 1을 얻을 수 있다.
하지만 n = 4, k = 3은 만족하는 경우가 없으므로 해당 경우만 예외로 처리해준다.
(3) 1 ≤ k ≤ n-2인 경우 :
앞서 i와 n-1을 & 연산하면 항상 i가 나온다고 했으므로, i의 원래 짝이었던 ((n-1)^i)과 n-1의 원래 짝이었던 0을 짝으로 만들어준다.
코드
#include <iostream>
using namespace std;
int t;
void solution() {
int n, k;
cin >> n >> k;
if (k == n - 1) {
if (n == 4)
cout << -1 << '\n';
else {
cout << n - 1 << " " << n - 2 << '\n';
cout << n / 2 - 1 << " " << 1 << '\n';
cout << 0 << " " << n / 2 << '\n';
for (int i = 2; i < n / 2 - 1; i++)
cout << i << " " << ((n - 1) ^ i) << '\n';
}
}
else if (k == 0) {
for (int i = 0; i < n / 2; i++)
cout << i << " " << ((n - 1) ^ i) << '\n';
}
else { // 1 <= k <= n-2 일 때
cout << n - 1 << " " << k << '\n';
cout << 0 << " " << ((n - 1) ^ k) << '\n';
for (int i = 0; i < n / 2; i++) {
if (i == 0 || i == k || i == ((n - 1) ^ k)) continue;
cout << i << " " << ((n - 1) ^ i) << '\n';
}
}
}
int main() {
cin >> t;
while (t--) {
solution();
}
}