高维前缀和
学了一发高维前缀和。
一般我们求多维前缀和是用容斥的,但是当维度很高时会很烦,这时就要用另一种求前缀和的方法。
打个比方,假设我们要求二维前缀和:
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]+=a[i][j-1];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
a[i][j]+=a[i-1][j];
这样就很好写了。而高维前缀和可以解决关于子集和超集统计的问题(复杂度为 O ( n ∗ 2 n ) O(n*2^n) O(n∗2n),而枚举子集的复杂度为 O ( 3 n ) O(3^n) O(3n)),只要把每一位看作一维进行操作就好了。
对于这道题,当 a & b = 0 a\&b=0 a&b=0时, a a a取反后是 b b b的超集,反之亦然。那么只需要做一遍高维前缀和就可以求出任意一个超集了。
代码:
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
#define F inline
using namespace std;
int n,a[N],s[1<<22];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
int main(){
n=_read();
for (int i=1;i<=n;i++) a[i]=_read(),s[~a[i]&(1<<22)-1]=a[i];
for (int i=0;i<22;i++)
for (int j=0;j<1<<22;j++)
if (!(j>>i&1)&&s[j|1<<i]) s[j]=s[j|1<<i];
for (int i=1;i<=n;i++)
printf("%d ",s[a[i]]?s[a[i]]:-1);
return 0;
}