1. 程式人生 > 實用技巧 >【洛谷P3812】【模板】線性基

【洛谷P3812】【模板】線性基

題目

題目連結:https://www.luogu.com.cn/problem/P3812
給定 \(n\) 個整數(數字可能重複),求在這些數中選取任意個,使得他們的異或和最大。

思路

一個值域為 \([0,A]\) 的序列的線性基長度為 \(\lceil\log A\rceil\)。並且通過線性基內元素互相異或,可以得到原序列中每一個數字。
具體的,我們設 \(d\)\(a\) 的線性基,那麼 \(d_i\) 表示 \(a\) 中二進位制最高位的 \(1\) 是第 \(i\) 位的一個數。假設我們要插入一個數 \(x\),那麼我們就不斷重複以下過程:

  1. 找到 \(x\) 的最高位的 \(1\)
    ,判斷該位置 \(d\) 是否有數字。如果沒有則賦值後直接 break。
  2. 否則設 \(x\) 最高位的 \(1\) 在位置 \(i\),那麼讓 \(x\gets x\ \mathrm{xor}\ d_i\)
  3. 如果 \(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;
}