問題
$N$ 本のベクトル $v_1, \ldots, v_N \in \mathbb{F}_2^{60}$(60 ビット非負整数で表現)と、重み $w_i$ が与えられる。
- 最大重み独立集合: ベクトルの集合のうち線形独立(XOR で 0 にならない)であり、重みの総和が最大のものを求めよ。
- $k$ 番目最大 XOR: $N$ 本のベクトル全体の線形生成空間において、$k$ 番目に大きい XOR 値を求めよ。
入力形式
N k
w_1 v_1
w_2 v_2
...
w_N v_N
制約
$1 \leq N \leq 2 \times 10^5$
$1 \leq k \leq 2^{60}$
$0 \leq v_i < 2^{60}$
$1 \leq w_i \leq 10^9$
入出力例
入力例 1
4 3
10 7
8 6
6 5
3 3
出力例 1
24
5
ヒント (段階的開示)
ヒント1: 方向性
問1: ガウス消去法(線形基底)を使ったグリーディ。重み降順でベクトルを処理し、現在の基底に追加できるなら追加する(マトロイド貪欲法)。問2: 線形基底を簡約化し、$k$ を 2進数と見て基底ベクトルの XOR を選択。
ヒント2: アプローチ
線形基底の構築:
basis[bit] = そのビットをピボットとするベクトル。新ベクトル追加時はガウス消去。簡約基底: 各基底ベクトルから上位ビットの基底を使って対応ビットを消す。$k$ 番目: 簡約基底を小さい順に並べ、$k$ の各ビットが 1 なら対応する基底を XOR。ヒント3: 誘導
class LinearBasis:
def __init__(self):
self.basis = {} # bit -> vector
def add(self, v):
for bit in range(59, -1, -1):
if not (v >> bit & 1):
continue
if bit not in self.basis:
self.basis[bit] = v
return True
v ^= self.basis[bit]
return False # 線形従属
def reduce(self):
for bit in sorted(self.basis):
for bit2 in self.basis:
if bit2 > bit and (self.basis[bit2] >> bit & 1):
self.basis[bit2] ^= self.basis[bit]
模範解答 (Python)
import sys
input = sys.stdin.readline
def solve():
N, k = map(int, input().split())
items = []
for _ in range(N):
line = list(map(int, input().split()))
w, v = line[0], line[1]
items.append((w, v))
# ---- 問1: 最大重み独立集合 ----
items.sort(key=lambda x: -x[0])
basis1 = {}
total_weight = 0
for w, v in items:
cur = v
for bit in range(59, -1, -1):
if not (cur >> bit & 1):
continue
if bit not in basis1:
basis1[bit] = cur
total_weight += w
break
cur ^= basis1[bit]
print(total_weight)
# ---- 問2: k番目最大 XOR ----
basis2 = {}
for _, v in items:
cur = v
for bit in range(59, -1, -1):
if not (cur >> bit & 1):
continue
if bit not in basis2:
basis2[bit] = cur
break
cur ^= basis2[bit]
# 簡約基底に変換
bits_sorted = sorted(basis2.keys())
for i, bit in enumerate(bits_sorted):
for j in range(i + 1, len(bits_sorted)):
bit2 = bits_sorted[j]
if basis2[bit2] >> bit & 1:
basis2[bit2] ^= basis2[bit]
r = len(bits_sorted)
basis_list = [basis2[b] for b in bits_sorted] # 昇順
# 降順で k番目 = 昇順で (2^r - k) 番目 (0-indexed)
idx = (1 << r) - k
ans = 0
for i in range(r):
if idx >> i & 1:
ans ^= basis_list[i]
print(ans)
solve()
Step-by-Step 解説
1線形基底(Linear Basis)の構築
GF(2) 上のガウス消去法。
GF(2) 上のガウス消去法。
basis[bit] はピボットが bit である基底ベクトル。新ベクトル $v$ の追加: 最上位ビット $b$ を見て、basis[b] が存在すれば XOR で消去。$v=0$ なら線形従属。2マトロイドと貪欲法
Vector Matroid の独立集合族は「GF(2) 上で線形独立な部分集合」。マトロイドの貪欲定理: 重み最大の独立集合は重み降順でグリーディに選べば得られる。
Vector Matroid の独立集合族は「GF(2) 上で線形独立な部分集合」。マトロイドの貪欲定理: 重み最大の独立集合は重み降順でグリーディに選べば得られる。
3簡約基底と k 番目 XOR
完全ガウス消去で各基底ベクトルを「ピボットビット以外の成分を全て 0」にする。span の各要素は基底ベクトルの XOR 部分集合と 1 対 1 対応。
完全ガウス消去で各基底ベクトルを「ピボットビット以外の成分を全て 0」にする。span の各要素は基底ベクトルの XOR 部分集合と 1 対 1 対応。
4降順での k 番目
昇順 0-indexed で $\text{idx} = 2^r - k$ 番目が、降順で $k$ 番目に大きい値。
昇順 0-indexed で $\text{idx} = 2^r - k$ 番目が、降順で $k$ 番目に大きい値。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
基底構築で v = 0 になった要素を追加 | 線形従属を見逃す | v == 0 なら break して追加しない |
| 簡約化を上から下だけ行う | 上位基底のビットを消さないと完全消去にならない | 下位基底も用いて上位基底のビットを消す |
| k 番目を 0-indexed で計算 | 問題は 1-indexed | idx = (1 << r) - k で調整 |
| ベクトルが 0 の場合の処理 | 0 は常に線形従属 | 0 は基底に追加しない |
次のステップ
- 発展問題: 動的にベクトルを追加しながら、各時点でのランク(基底サイズ)と span の $k$ 番目最大値をオンラインで答えよ