티스토리 뷰

한동안 학교 일로 바빴는데, 앞으로 문제를 더 열심히 풀어야겠다고 생각했다. 원래는 문제 여러 개를 풀고 묶어서 글로 썼는데, 이제부터는 더 문제 수가 적은 글들도 올라갈 것 같다.

Subtask 1

$N = 4$이고 $L \le 2$턴 안에 해결해야 한다.

 

처음에 자신이 parent인지 아는 사람은 딱히 문제가 없다. 나머지 사람은 $3$명이 있는데, 이들은 자신을 제외한 두 명에게 parent인지 물어볼 수 있고, 둘 다 parent가 아니라고 하면 질문을 안 해본 한 사람을 고르면 된다.

 

void st1(){
    const int n = 4, L = 2;
    printf("2\n");
    for(int P=1; P<=n; P++){
        printf("%d\n", P);
        for(int C=1; C<=n; C++){
            vector<int> who (n+1); /// 0: 모름, 1: 맞음, -1: 아님
            if(P==C) who[C] = 1;
            else who[C] = -1;
            for(int R=1; R<=L; R++){
                printf("%c ", P==C ? 'T' : 'F');
                int x = 1;
                while(who[x]) x++;
                printf("%d ", x);
                if(P==x) who[x] = 1;
                else who[x] = -1;
            }
            puts("");
        }
    }
}

 

Subtask 2

$N = 32$이고 $L \le 8$턴 안에 해결해야 한다. 아무래도 $\log N$ 정도에 비례하는 풀이를 찾아야 할 것 같다. 그렇다면 $L$이 $\log N + 3$인지 $2 \log N - 2$인지 조금 생각해 볼 필요가 있는데, Subtask 1에서 $L = 2 \log N - 2$였으므로 이 시간 복잡도로 풀면 좋을 것 같다.

 

그렇다면 실질적으로 얻을 수 있는 정보가 어떤 게 있는지 생각해 보자. 일단 가장 먼저 떠오르는 건 첫 라운드를 어떻게 하는가이다. 아무래도 xor이 $1$인 것들끼리 묶어서 하면 좋을 것 같다. (인덱스가 대충 $[0, 31]$ 범위라고 생각할 때의 이야기이다.) 이렇게 하면 parent가 $p$일 때, $p$와 $p \oplus 1$이 메시지 T를 본 상태가 된다. 이걸 빠르게 전파시키려면 어떻게 할까? 다음 턴에는 xor이 $2$인 걸 보면 된다. 그럼 이제 $p, p \oplus 1, p \oplus 2, p \oplus 3$이 T를 본 상태가 된다. 이걸 $4$, $8$에 대해서도 해 보자. 이러면 노드들이 어떤 정보를 얻을 수 있을까?

 

parent가 $p$일 때, 어떤 노드 $c$에 대해 $p$와 $c$의 이진수를 비교하자. 이때 가장 높은 비트부터 봤을 때 처음으로 다른 비트가 $2^b$의 비트라고 하자. 이때 $c$는 $b+1$번 턴에 처음으로 메시지 T를 본 상태가 된다는 것을 알 수 있다. 만약 네 턴 동안 T를 못 받았다면, 애초에 $2^4$의 비트가 다르다는 것을 알 수 있다.

 

여기서부터는 어떻게 할까? $p$와 $c$에서 다른 가장 높은 비트가 $b(p, c)$번 비트라고 하자. 이때 $b(p, c) \le 3$인 것들은 $p$의 $2^3$의 비트가 무엇인지를 알 것이다. 한 번의 라운드를 써서 이것을 $b(p,c) = 4$인 것에게 전달한다. 그러면 $b(p, c) = 4$인 것들은 $c \oplus 2^4$번 노드를 들으면 저 값을 알 수 있다. 한 번의 라운드를 사용한 뒤, 이제 모든 노드가 $p$의 $2^4, 2^3$의 자리 비트가 뭔지 안다. 여기서 $2^2$의 자리 비트가 무엇인지 알려면 이 과정을 비트를 하나 내려서 반복하면 된다. $b(p, c) \le 2$인 것들은 $p$의 $2^2$의 자리 비트를 전달한다. $b(p, c) \ge 3$인 것들은 앞에서 $2^4$, $2^3$의 자리 비트를 알아냈으므로, $b(p, c) \le 2$인 $c$ 하나를 생성하는 것이 가능하다. (그냥 $2^4$, $2^3$의 자리 비트를 사용하고 뒤 세 자리는 $0$으로 두면 될 것이다.) 그럼 해당 플레이어에게 $p$의 $2^2$ 자리 비트가 무엇인지도 들을 수 있다. 이 과정을 비트를 내리며 두 번 더 반복하면, $4$번의 턴을 더 써서 모든 플레이어가 $p$를 알아낼 수 있다.

 

void st2(){
    const int n = 32, L = 8, B = 4;
    printf("%d\n", L);
    for(int P=0; P<n; P++){
        printf("%d\n", P+1);
        for(int C=0; C<n; C++){
            if(C == P){
                for(int i=0; i<B; i++) printf("T %d ", C+1);
                /// 뒤 log N-1 번 턴에는 parent도 규칙에 맞게 전송해야 함을 유의하자
                for(int i=B-1; i>=0; i--) printf("%c %d ", ((P >> i) & 1) ? 'T' : 'F', P + 1);
                puts("");
                continue;
            }
            int lb = B;
            while(((P>>lb)&1) == ((C>>lb)&1)) lb--;

            for(int i=0; i<B; i++){
                printf("%c %d ", i>lb ? 'T' : 'F', (C ^ (1<<i)) + 1);
            }
            for(int i=B-1; i>=0; i--){
                int v = (2<<i)-1;
                /// C가 p의 i번 비트를 알 조건은 lb <= i이다
                /// 아는 경우에는 그 비트를 전송하고, 모르면 0을 전송한다
                printf("%c %d ", ((lb > i ? 0 : ((P >> i) & 1)) ? 'T' : 'F'), ((P & ~v) + 1));
            }
            puts("");
        }
    }
}

 

Subtask 3

Subtask 2와 느낌은 비슷하나, 한 턴을 더 쓸 수 있고, 그 대가로 $N$이 $1.5$배 커졌다.

 

일단 $N$이 $2$의 완벽한 거듭제곱이 아닌 것부터 좀 불편하다. 그래도 $2^4 \times 3$이니까, Subtask 2의 전반부를 돌리는 데는 문제가 없다.

 

각 플레이어의 번호를 $5$비트로 보되, 첫 자리에 $0, 1, 2$가 들어갈 수 있다고 생각하자. Subtask 2의 전반부를 네 턴에 걸쳐 실행하면, $2^4$의 비트가 같은 수들은 정보를 조금 얻지만, $2^4$의 비트가 다른 것들은 그 다른 $2^4$의 비트에 대해 두 개의 후보가 생긴다. (즉, $p$의 $2^4$ 비트가 $0$이라 할 때, 네 턴이 지난 이후 $c$의 $2^4$ 비트가 $1$인 $c$들은 $p$의 $2^4$ 비트가 $0$인지 $2$인지 구분할 수 없다.)

 

한 턴을 더 써서 이 부분이 가능하게 할 것이다. 일단 $b(p, c) \le 3$인 것들은 모두 메시지를 받은 상태이다. 이제 마지막 한 턴은 cyclic하게 구성을 해서, $2^4$ 자리 비트가 $0$인 것들은 그 자리를 $1$로 바꾼 것들을 확인하고, $1$인 것들은 $2$로 바꾼 것들을 확인하고, $2$인 것들은 $0$으로 바꾼 것들을 확인한다. 이렇게 하면, $b(p, c) = 4$인 것들은 자신이 본 쪽이 T인지 F인지를 통해 $p$의 $2^4$ 비트를 알아낼 수 있다.

 

나머지 과정은 Subtask 2와 같다.

 

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

void st1();
void st2();
void st3();

int main(){
    int n;
    cin>>n;
    if(n==4) st1();
    else if(n==32) st2();
    else st3();
}

void st1(){
    const int n = 4, L = 2;
    printf("2\n");
    for(int P=1; P<=n; P++){
        printf("%d\n", P);
        for(int C=1; C<=n; C++){
            vector<int> who (n+1); /// 0: 모름, 1: 맞음, -1: 아님
            if(P==C) who[C] = 1;
            else who[C] = -1;
            for(int R=1; R<=L; R++){
                printf("%c ", P==C ? 'T' : 'F');
                int x = 1;
                while(who[x]) x++;
                printf("%d ", x);
                if(P==x) who[x] = 1;
                else who[x] = -1;
            }
            puts("");
        }
    }
}

void st2(){
    const int n = 32, L = 8, B = 4;
    printf("%d\n", L);
    for(int P=0; P<n; P++){
        printf("%d\n", P+1);
        for(int C=0; C<n; C++){
            if(C == P){
                for(int i=0; i<B; i++) printf("T %d ", C+1);
                /// 뒤 log N-1 번 턴에는 parent도 규칙에 맞게 전송해야 함을 유의하자
                for(int i=B-1; i>=0; i--) printf("%c %d ", ((P >> i) & 1) ? 'T' : 'F', P + 1);
                puts("");
                continue;
            }
            int lb = B;
            while(((P>>lb)&1) == ((C>>lb)&1)) lb--;

            for(int i=0; i<B; i++){
                printf("%c %d ", i>lb ? 'T' : 'F', (C ^ (1<<i)) + 1);
            }
            for(int i=B-1; i>=0; i--){
                int v = (2<<i)-1;
                /// C가 p의 i번 비트를 알 조건은 lb <= i이다
                /// 아는 경우에는 그 비트를 전송하고, 모르면 0을 전송한다
                printf("%c %d ", ((lb > i ? 0 : ((P >> i) & 1)) ? 'T' : 'F'), ((P & ~v) + 1));
            }
            puts("");
        }
    }
}

void st3(){
    const int n = 48, L = 9, B = 4;
    printf("%d\n", L);
    for(int P=0; P<n; P++){
        printf("%d\n", P+1);
        for(int C=0; C<n; C++){
            if(C == P){
                for(int i=0; i<=B; i++) printf("T %d ", C+1);
                /// 뒤 log N-1 번 턴에는 parent도 규칙에 맞게 전송해야 함을 유의하자
                for(int i=B-1; i>=0; i--) printf("%c %d ", ((P >> i) & 1) ? 'T' : 'F', P + 1);
                puts("");
                continue;
            }
            int lb = B+1, S = C>>B, T = C&((1<<B)-1);
            while(((P>>lb)&1) == ((C>>lb)&1)) lb--;
            if(lb==B+1) lb = B;

            for(int i=0; i<B; i++){
                printf("%c %d ", i>lb ? 'T' : 'F', (C ^ (1<<i)) + 1);
            }
            printf("%c %d ", lb < B ? 'T' : 'F', (((S+1)%3)<<4 | T) + 1);

            for(int i=B-1; i>=0; i--){
                int v = (2<<i)-1;
                /// C가 p의 i번 비트를 알 조건은 lb <= i이다
                /// 아는 경우에는 그 비트를 전송하고, 모르면 0을 전송한다
                printf("%c %d ", ((lb > i ? 0 : ((P >> i) & 1)) ? 'T' : 'F'), ((P & ~v) + 1));
            }
            puts("");
        }
    }
}

'문제풀이 > BOJ' 카테고리의 다른 글

BOJ 34105 Bitaro's Travel 2 (JOISC 2024-2025 Day 1 P3)  (0) 2025.11.13
BOJ 34107 Collecting Stamps 4 (JOISC 2024-2025 Day 2 P2)  (0) 2025.11.12
BOJ 31584. 전구 게임  (0) 2025.01.23
BOJ 10350. 은행  (0) 2024.06.22
BOJ 25015. 아이싱  (0) 2024.05.22
공지사항
최근에 올라온 글
Total
Today
Yesterday