CF165E Compatible Numbers
题面:
题面翻译
有两个整数a,b。如果a&b=0,那么我们称a与b是相容的。比如90(1011010)与36(100100)相容。给出一个序列aa ,你的任务是对于每个ai,找到在序列中与之相容的aj 。如果找不到这样的数,输出-1。
贡献者:An_Account1 ≤ n ≤ 1 0 6 , 1 ≤ a i ≤ 4 × 1 0 6 1 \leq n \leq 10^6,1\leq a_i \leq 4\times 10^6 1≤n≤106,1≤ai≤4×106
题目描述
Two integers $ x $ and $ y $ are compatible, if the result of their bitwise “AND” equals zero, that is, $ a $ $ & $ $ b=0 $ . For example, numbers $ 90 $ $ (1011010_{2}) $ and $ 36 $ $ (100100_{2}) $ are compatible, as $ 1011010_{2} $ $ & $ $ 100100_{2}=0_{2} $ , and numbers $ 3 $ $ (11_{2}) $ and $ 6 $ $ (110_{2}) $ are not compatible, as $ 11_{2} $ $ & $ $ 110_{2}=10_{2} $ .
You are given an array of integers $ a_{1},a_{2},…,a_{n} $ . Your task is to find the following for each array element: is this element compatible with some other element from the given array? If the answer to this question is positive, then you also should find any suitable element.
输入格式
The first line contains an integer $ n $ ( $ 1<=n<=10^{6} $ ) — the number of elements in the given array. The second line contains $ n $ space-separated integers $ a_{1},a_{2},…,a_{n} $ ( $ 1<=a_{i}<=4·10^{6} $ ) — the elements of the given array. The numbers in the array can coincide.
输出格式
Print $ n $ integers $ ans_{i} $ . If $ a_{i} $ isn’t compatible with any other element of the given array $ a_{1},a_{2},…,a_{n} $ , then $ ans_{i} $ should be equal to -1. Otherwise $ ans_{i} $ is any such number, that $ a_{i} $ $ & $ $ ans_{i}=0 $ , and also $ ans_{i} $ occurs in the array $ a_{1},a_{2},…,a_{n} $ .
样例 #1
样例输入 #1
2 90 36
样例输出 #1
36 90
样例 #2
样例输入 #2
4 3 6 3 6
样例输出 #2
-1 -1 -1 -1
样例 #3
样例输入 #3
5 10 6 9 8 2
样例输出 #3
-1 8 2 2 8
高维前缀和!
简单的说,普通的前缀和( 1 ∼ 3 1\sim 3 1∼3 维),我们经常使用容斥
但是,维度一高,容斥的复杂度大的吓人!
我们得换一种全新的方法!————高维前缀和 闪亮登场!
我们不妨来考虑这样一个事情:
我们的终极目标是把 1 ≤ i ≤ x , 1 ≤ j ≤ y , … 1\leq i \leq x,1\leq j \leq y,\ldots 1≤i≤x,1≤j≤y,… 上的所有点累加到 ( x , y , … ) (x,y,\ldots) (x,y,…) 上
那么,我们可以像著名游戏 2048 那样,每次直接搞定一个维度,将所有维度搞定之后,前缀和就做完了
以二维举例:
假设我们的原数组是这样的:
我们对
x
x
x 维先做一个前缀和:
然后再对
y
y
y 维做一个前缀和:
我们再举一个例子,加深印象:
x
−
T
r
a
n
s
f
o
r
m
:
x-Transform:
x−Transform:
y
−
T
r
a
n
s
f
o
r
m
:
y-Transform:
y−Transform:
回到正题,高维前缀和和这个有什么关系呢?
高维前缀和还有子集求和的功能!
对于一个 S ( 2 ) \mathbf{S}(2) S(2) 的 01 01 01 串,我们让 f S = ∑ S ′ ∈ S a S ′ f_{\mathbf{S}} = \sum_{\mathbf{S^\prime} \in \mathbf{S}} a_{\mathbf{S^\prime}} fS=∑S′∈SaS′
根据高维前缀和的搞法,可以得到
f
S
=
∑
i
∈
S
f
S
⊕
2
i
f_{\mathbf{S}} = \sum_{i \in \mathbf{S}} f_{\mathbf{S}\oplus 2^i}
fS=i∈S∑fS⊕2i
这道题,我们发现, a & b = 0 ⇔ b ⊆ ( a ⊕ U ) a\And b = 0 \Leftrightarrow b \subseteq (a \oplus U) a&b=0⇔b⊆(a⊕U)
那么,对于一个数,我们只需要找到ta补集子集中可行的的一个答案就可以了
对于一个数 s s s,我们将 f s = s f_s = s fs=s;
我们将 1 ∼ 2 22 ( 4 × 1 0 6 ≤ 2 22 ≤ 5 × 1 0 6 ) 1 \sim 2^{22}(4\times 10^6 \leq 2^{22} \leq 5\times 10^6) 1∼222(4×106≤222≤5×106) 的 f i f_i fi 做一次"高维前缀和"(非狭义中的和,指保存可能的答案之一)
最后对询问 a a a,访问 U ⊕ a \mathbf{U} \oplus a U⊕a 子集中是否有答案即可!
AC-code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
int x = 0, w = 1;
char ch = 0;
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch - '0');
ch = getchar();
}
return x * w;
}
void wt(int x) {
static int sta[35];
int f = 1;
if(x < 0) f = -1,x *= f;
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
if(f == -1) putchar('-');
while (top) putchar(sta[--top] + 48);
}
const int N = 1e6+5,M = 5e6+5,U = (1 << 22) - 1;
int a[N],f[M],n;
signed main() {
n = rd();
for(int i = 1;i<=n;i++) {
a[i] = rd();
f[a[i]] = a[i];
}
for(int i = 0;i<=21;i++)
for(int j = 0;j<(1<<22);j++)
if((j & (1 << i)) && f[j ^ (1 << i)])
f[j] = f[j ^ (1<<i)];
for(int i = 1;i<=n;i++) {
int b = U ^ a[i];
wt(f[b] ? f[b] : -1);putchar(' ');
}
return 0;
}
这种东西叫做 S O S d p SOS\ dp SOS dp 高维前缀和-动态规划
本博客:
监督:MingJunYi
超监督:凉宫春日