1 条题解
-
0
思路与算法
AND:从高位到低位试加答案位,若至少两个数包含当前候选所有位就保留。XOR:把前面的数放二进制字典树,当前数每位优先走相反分支。OR:设freq为数值频次,子集zeta得C[M]=落在M子集中的数个数;C[M]²计有序配对的OR为M子集的数量,Möbius反演得每个精确OR结果的配对数,扣除相同下标,再找最高非零结果。
正确性说明
AND候选可行等价于至少两数同时包含它,按高位优先贪心保证最大整数。XOR每一位的1优先于所有低位,因此字典树逐位选相反分支最优,且只查先前数保证下标不同。两数OR为M子集当且仅当两数均为M子集,故zeta后的平方是正确累计配对数;Möbius逆变换恢复精确结果。自配对OR就是自身值,扣freq后恰留下不同下标配对,取最大有配对结果即可。
复杂度
B=20。AND与XOR各O(NB)时间;OR为O(B2^B+N),数组初始化O(2^B)。XOR空间O(NB),OR空间O(2^B),AND除输入外O(1)。最多T6组顺序释放。
易错点
c2才是xor、c3是or。相同数值但不同下标允许;OR要扣自配对,XOR查询前先插入会误用自身。变换中计数平方达10^10,使用64位。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; using ll = long long; const int B = 20, S = 1 << B; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n, c; cin >> n >> c; vector<int> a(n); for (int &x : a) cin >> x; int ans = 0; if (c == 1) { for (int k = B - 1; k >= 0; k--) { int z = ans | (1 << k), cnt = 0; for (int x : a) cnt += (x & z) == z; if (cnt >= 2) ans = z; } } else if (c == 2) { vector<array<int, 2>> tr(1, {{-1, -1}}); for (int x : a) { if (tr.size() > 1) { int u = 0, z = 0; for (int k = B - 1; k >= 0; k--) { int b = x >> k & 1; if (tr[u][b ^ 1] >= 0) { z |= 1 << k; u = tr[u][b ^ 1]; } else u = tr[u][b]; } ans = max(ans, z); } int u = 0; for (int k = B - 1; k >= 0; k--) { int b = x >> k & 1; if (tr[u][b] < 0) { tr[u][b] = tr.size(); tr.push_back({{-1, -1}}); } u = tr[u][b]; } } } else { vector<ll> f(S); for (int x : a) f[x]++; for (int d = 1; d < S; d <<= 1) for (int base = 0; base < S; base += d * 2) for (int j = 0; j < d; j++) f[base + d + j] += f[base + j]; for (ll &x : f) x *= x; for (int d = 1; d < S; d <<= 1) for (int base = 0; base < S; base += d * 2) for (int j = 0; j < d; j++) f[base + d + j] -= f[base + j]; for (int x : a) f[x]--; for (int x = S - 1; x >= 0; x--) if (f[x]) { ans = x; break; } } cout << ans << '\n'; } }
- 1