給定 n 個數 (≤10⁵),每個數 ≤ 4×10⁶ (22 位)。對每個數 aᵢ,找另一個數 aⱼ 使 aᵢ & aⱼ = 0。若不存在輸出 -1。暴力 O(n²) 超時。
aᵢ & aⱼ = 0 ⇔ aⱼ ⊆ ~aᵢ (在 22 位範圍內)。即 aⱼ 是 aᵢ 取反的子集。預處理:對每個掩碼 m,記錄是否存在數等於 m 的超集。從大掩碼向小掩碼遞推:dp[mask] = dp[mask] || dp[mask | (1<<i)]。
S = (1<<22) - 1dp[x ^ S] = x (x 的互補掩碼記錄 x)dp[mask] 為空,嘗試從添加一位的超集繼承值dp[x] (即與 x 兼容的某個數)#include <iostream>
#include <cstring>
using namespace std;
const int MX = (1 << 22) + 5;
int n, dp[MX], g[MX];
int main() {
while (~scanf("%d", &n)) {
int S = (1 << 22) - 1;
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; ++i) {
scanf("%d", &g[i]);
dp[g[i] ^ S] = g[i]; // 記錄互補掩碼對應的值
}
for (int i = S; i >= 0; --i) { // 從大到小枚舉掩碼
if (!dp[i]) {
for (int j = 0; j < 22; ++j) // 嘗試添加一位
if (dp[i | (1 << j)])
dp[i] = dp[i | (1 << j)];
}
}
for (int i = 0; i < n; ++i) {
if (i) cout << " ";
if (dp[g[i]]) cout << dp[g[i]];
else cout << "-1";
}
cout << endl;
}
return 0;
}
x ^ S 而非 ~x (避免符號位)