【洛谷P3812】【模板】線性基
阿新 • • 發佈:2021-01-03
題目
題目連結:https://www.luogu.com.cn/problem/P3812
給定 \(n\) 個整數(數字可能重複),求在這些數中選取任意個,使得他們的異或和最大。
思路
一個值域為 \([0,A]\) 的序列的線性基長度為 \(\lceil\log A\rceil\)。並且通過線性基內元素互相異或,可以得到原序列中每一個數字。
具體的,我們設 \(d\) 是 \(a\) 的線性基,那麼 \(d_i\) 表示 \(a\) 中二進位制最高位的 \(1\) 是第 \(i\) 位的一個數。假設我們要插入一個數 \(x\),那麼我們就不斷重複以下過程:
- 找到 \(x\) 的最高位的 \(1\)
- 否則設 \(x\) 最高位的 \(1\) 在位置 \(i\),那麼讓 \(x\gets x\ \mathrm{xor}\ d_i\)。
- 如果 \(x\) 為 \(0\) 則直接退出。
因為每次操作二後 \(x\) 最高位的 \(1\) 都會變成 \(0\),所以複雜度是 \(O(\log x)\) 的。
這題要求異或和最大,我們就從高位到低位列舉,如果此時的答案 \(res\) 第 \(i\) 位為 \(0\),那麼就讓 \(res\gets res\ \mathrm{xor}\ d_i\)。這樣顯然每次操作不會更劣。
時間複雜度 \(O(n\log n)\)
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=55; int n; ll x,d[N]; bool flag; void insert(ll x) { for (int i=N-1;i>=0;i--) if (x&(1LL<<i)) { if (!d[i]) { d[i]=x; return; } x^=d[i]; } flag=1; } ll getmax() { ll ans=0; for (int i=N-1;i>=0;i--) if (!(ans&(1LL<<i))) ans^=d[i]; return ans; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%lld",&x); insert(x); } printf("%lld",getmax()); return 0; }