题目链接:点我啊╭(╯^╰)╮
题目大意:
给长度为
n
n
n 的数组
A
A
A,
A
i
≤
2
18
A_i ≤ 2^{18}
Ai≤218
选出
i
i
i 个数字
a
1
.
.
.
a
i
a_1 ... a_i
a1...ai,使得
a
1
⨁
a
2
⨁
.
.
.
⨁
a
i
a_1\bigoplus a_2 \bigoplus ... \bigoplus a_i
a1⨁a2⨁...⨁ai 最大
每个数字可以重复选,求
i
i
i 从
1
1
1 到
n
n
n 的答案
解题思路:
由
A
i
≤
2
18
A_i ≤ 2^{18}
Ai≤218,可以看出选出
18
18
18 个数字就能得到能表示的最大值
因此只需要处理
i
≤
19
i≤19
i≤19 的所有答案,
i
≥
20
i≥20
i≥20 时,
a
n
s
i
=
a
n
s
i
−
2
ans_i = ans_{i-2}
ansi=ansi−2
然后将
a
i
a_i
ai 作为下标记为
A
A
A,每与
A
A
A 作一遍
F
W
T
FWT
FWT,就相当于多选了一个数字
然后求下标最大且值为
1
1
1 的下标,即为当前选了
i
i
i 个数字的答案
注意每次做完之后,要将值改为
1
1
1,避免出现取模为
0
0
0 的情况
同时取模操作常数较大,需要适当修改
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 998244353;
const int INV = 499122177;
const int maxn = (1 << 18) + 10;
int n, a[maxn], b[maxn], ans[maxn];
inline int add(int x, int y) {
return (x + y) % mod;
int ret = x + y; while(ret >= mod) ret -= mod;
while(ret < 0) ret += mod; return ret;
}
inline void fwt(int *f, int type) { // xor
int x, y;
for(int mid=1; mid<1<<18; mid<<=1)
for(int j=0,len=mid<<1; j<1<<18; j+=len)
for(int k=j; k<mid+j; ++k) {
x = f[k]; y = f[k+mid];
f[k] = add(x, y); f[k+mid] = add(x, -y);
if(type==-1) f[k]=(ll)f[k]*INV%mod, f[k+mid]=(ll)f[k+mid]*INV%mod;
}
}
signed main() {
scanf("%d", &n);
for(int i=1, x; i<=n; ++i) scanf("%d", &x), a[x] = 1;
fwt(a, 1); b[0] = 1;
for(int i=1; i<20; ++i) {
fwt(b, 1);
for(int j=0; j<1<<18; ++j) b[j] = 1ll * b[j] * a[j] % mod;
fwt(b, -1);
for(int j=0; j<1<<18; ++j)
if(b[j]) {
ans[i] = j;
b[j] = 1;
}
}
for(int i=1; i<=n; ++i) {
if(i > 2) ans[i] = max(ans[i-2], ans[i]);
printf("%d ", ans[i]);
}
}