C++で整数ストリームから指定した整数との最大XORを求める方法
この問題では、以下のいずれかのタイプに該当するQ個のクエリが与えられます。
タイプ1 − 挿入 (1, i):値iを持つ要素をデータ構造に追加します。
タイプ2 − findXOR (2, i):データ構造内のすべての要素と要素iとのXORの最大値を求めます。
データ構造には、初期状態として要素0のみが含まれているものとします。
問題を理解するための例を見てみましょう。
入力
Queries: (1, 9), (1, 3), (1, 7), (2, 8), (1, 5), (2, 12)
出力
15 15
説明
各クエリを順に処理すると、
(1, 9) => データ構造 => {9}
(1, 3) => データ構造 => {9, 3}
(1, 7) => データ構造 => {9, 3, 7}
(2, 8) => 最大XOR(_, 8) = 15(XOR(7, 8))
(1, 5) => データ構造 => {9, 3, 7, 5}
(2, 12) => 最大XOR(_, 12) = 15(XOR(3, 12))解法アプローチ
この問題は、トライ(trie)と呼ばれる特殊な探索木データ構造を使うことで効率的に解くことができます。ここで使うトライは、各ノードが2つの子ノードを持ち、数値を2進数のビット列として格納するものです。
具体的な手順は次のとおりです。
- タイプ1のクエリが来た場合は、その数値の2進表現(32ビット分)を上位ビットから順にトライへ挿入します。
- タイプ2のクエリが来た場合は、与えられた値の各ビットに対応するパスをトライ上で探索します。このとき、可能な限り「反対のビット」の子ノードを選ぶことで、その桁のXORを1にでき、結果として最大XORが得られます。
この手法により、挿入も検索も1クエリあたりO(32)、すなわちビット長に比例した時間で処理できます。
トライについてさらに詳しく知りたい方は、「トライデータ構造」の解説記事をご参照ください。
ソリューションの動作を示すプログラム:
例
#include<bits/stdc++.h>
using namespace std;
struct Trie {
Trie* children[2];
bool isLeaf;
};
bool check(int N, int i) {
return (bool)(N & (1<<i));
}
Trie* newNode() {
Trie* temp = new Trie;
temp->isLeaf = false;
temp->children[0] = NULL;
temp->children[1] = NULL;
return temp;
}
void insertVal(Trie* root, int x) {
Trie* val = root;
for (int i = 31; i >= 0; i--) {
int f = check(x, i);
if (! val->children[f])
val->children[f] = newNode();
val = val->children[f];
}
val->isLeaf = true;
}
int solveQueryType2(Trie *root, int x){
Trie* val = root;
int ans = 0;
for (int i = 31; i >= 0; i--) {
int f = check(x, i);
if ((val->children[f ^ 1])){
ans = ans + (1 << i);
val = val->children[f ^ 1];
}
else
val = val->children[f];
}
return ans;
}
void solveQueryType1(Trie *root, int x){
insertVal(root, x);
}
int main(){
int Q = 6;
int query[Q][2] = {{1, 9}, {1, 3}, {1, 7}, {2, 8}, {1, 5}, {2, 12}};
Trie* root = newNode();
for(int i = 0; i < Q; i++){
if(query[i][0] == 1 ){
solveQueryType1(root, query[i][1]);
cout<<"Value inserted to the data Structure. value = "<<query[i][1]<<endl;
}
if(query[i][0] == 2){
cout<<"The maximum XOR with "<<query[i][1]<<" is "<<solveQueryType2(root, query[i][1])<<endl;
}
}
return 0;
}出力
Value inserted to the data Structure. value = 9 Value inserted to the data Structure. value = 3 Value inserted to the data Structure. value = 7 The maximum XOR with 8 is 15 Value inserted to the data Structure. value = 5 The maximum XOR with 12 is 15
-
C++で指定された値に最も近いk個の要素を検索する方法
いくつかの要素を含む配列 A があるとします。ここに、値 X と整数 k も与えられます。この課題は、配列 A の中から X に最も近い k 個の要素を見つけることです。なお、X が配列内に存在する場合は、その要素自体は出力に含めません。 例として、A = [12, 16, 22, 30, 35, 39, 42, 45, 48, 50, 53, 55, 56]、X = 35、k = 4 とすると、出力は「30, 39, 42, 45」になります。 解法の考え方:二分探索を活用する この問題を効率的に解くには、二分探索(バイナリサーチ)の手法を利用します。二分探索によって「クロスオーバーポイ
-
C++で積がPとなるN個の整数の最大GCDを求める方法
2つの整数 N と P が与えられているとします。P は N 個の未知の整数の積であり、そのときそれらの整数の最大公約数(GCD)としてあり得る最大値を求めるのが課題です。 例として、N = 3、P = 24 の場合を考えてみましょう。3つの整数の組み合わせとしては {1, 1, 24}、{1, 2, 12}、{1, 3, 8}、{1, 4, 6}、{2, 2, 6}、{2, 3, 4} などが考えられます。それぞれのGCDは 1, 1, 1, 1, 2, 1 となるため、この場合の答えは 2 です。 解法のアプローチ まず P のすべての素因数を求め、ハッシュマップに格納します。各素因数が