BOJ/Platinum ~ Diamond

[BOJ/Platinum 3] 백준 13504 XOR 합(C++)

보단잉 2023. 5. 3. 19:19

문제 링크

 

문제

N개의 수로 이루어진 수열 A가 주어진다. 

수열 A에서 연속된 부분 수열을 고르려고 한다. 부분 수열의 XOR 합이란, 부분 수열에 들어있는 모든 원소를 XOR한 값을 의미한다.

수열 A가 주어졌을 때, XOR 합이 가장 큰 부분 수열을 찾는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. (1 ≤ T ≤ 10)

각 테스트 케이스의 첫째 줄에는 배열의 크기 N (1 ≤ N ≤ 100,000), 둘째 줄에는 수열 A에 들어있는 수가 주어진다. 수열 A에 들어있는 수는 32비트 부호있는 정수 범위 안에 들어가는 음이 아닌 정수이다.

출력

각각의 테스트 케이스마다 수열 A의 연속된 부분 수열 중에서 XOR 합이 가장 큰 부분 수열의 XOR 합을 출력한다.

예제 입력 1

2
5
3 7 7 7 0
5
3 8 2 6 4

예제 출력 1

7
15

알고리즘 분류

  • 트라이

풀이

0과 수열 A에 존재하는 정수들의 순차적인 누적 XOR 합들을 트라이에 insert한다.

그리고 0과 수열 A에 존재하는 정수들의 순차적인 누적 XOR 합들을 통해 트라이를 탐색하는 것으로 XOR 합의 최댓값을 구할 수 있다.

최댓값을 구하는 방법은 다음과 같다.

값이 크기 위해서는 비교할 두 비트가 서로 다른 값이어야 한다. 결과가 1로 나오는데, 1이 많을수록 값이 높아지기 때문이다.

또한 최대한 많은 1이 존재해야 한다. 따라서 가장 높은 자릿수부터 탐색해나가며 현재 비트와 1을 XOR한 결과가 있다면 해당 자식 노드로 넘어간다.

리프 노드까지 도달했다면 기록되어 있는 정수와 XOR 연산을 하고 이 결과값의 최댓값을 구한다.

코드

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#define LL long long
#define FASTIO cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);

using namespace std;
struct TRIE {
    bool isEnd;
    LL end_Number;
    TRIE* Child[2];

    TRIE() {
        isEnd = false;
        for (int i = 0; i < 2; i++) {
            Child[i] = nullptr;
        }
    }

    ~TRIE() {
        for (int i = 0; i < 2; i++) {
            delete Child[i];
        }
    }

    void insert_Pattern(string Pattern, LL Number) {
        TRIE* NowTrie = this;

        for (int i = 0; i < Pattern.size(); i++) {
            int Now = Pattern[i] - '0';
            if (NowTrie->Child[Now] == nullptr) {
                NowTrie->Child[Now] = new TRIE;
            }
            NowTrie = NowTrie->Child[Now];
        }

        NowTrie->isEnd = true;
        NowTrie->end_Number = Number;
    }
    
    LL query(string NowS, LL NowN) {
        TRIE* NowTrie = this;

        for (int i = 0; i < NowS.size(); i++) {
            int Now = NowS[i] - '0';

            if (NowTrie->Child[Now ^ 1] != nullptr) {
                NowTrie = NowTrie->Child[Now ^ 1];
            }
            else {
                NowTrie = NowTrie->Child[Now];
            }
        }

        return (NowTrie->end_Number ^ NowN);
    }
};

int T, N;
LL A, K;
int max_Length;
vector<int> Numbers;
vector<string> Strings;
LL Answer;

void init() {
    Numbers.clear();
    Strings.clear();
    K = 0;
    max_Length = 0;
    Answer = 0;
}

string make_String(int Number) {
    string Result = "";
    while (1) {
        if (Number == 0) {
            break;
        }
        Result = to_string(Number % 2) + Result;
        Number /= 2;
    };
    return Result;
}

void settings(TRIE* Root) {
    for (int i = 0; i <= N; i++) {
        Answer = max(Answer, Root->query(Strings[i], Numbers[i]));
    }
}

void find_Answer() {
    cout << Answer << "\n";
}

void input() {
    cin >> T;
    while (T--) {
        init();
        TRIE* Root = new TRIE();
        cin >> N;
        Numbers.push_back(K);
        Strings.push_back("0");
        for (int i = 0; i < N; i++) {
            cin >> A;
            K ^= A;
            Numbers.push_back(K);
            string NewK = make_String(K);
            max_Length = max(max_Length, (int)NewK.size());
            Strings.push_back(NewK);
        };
        for (int i = 0; i <= N; i++) {
            int Sub = max_Length - (int)Strings[i].size();
            while (Sub--) {
                Strings[i] = "0" + Strings[i];
            };
            Root->insert_Pattern(Strings[i], Numbers[i]);
        }
        settings(Root);
        find_Answer();
        delete Root;
    };
}

int main() {
    FASTIO
    input();

    return 0;
}