智乃酱的子集与超集
Sol:
高维前缀和
从二维前缀和的另一种方法扩展的高维。
普通的二维前缀和需要容斥。另一种方法是先处理行的前缀和,再处理列的前缀和,算两次,不需要容斥。
高位前缀和同样运用了这个原理。
储存一下代码当板子。
Code:
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dep(i,a,b) for(int i=(a);i>=(b);--i)
#define lowbit(x) (x&(-x))
#define debug(x) cout<<#x<<" :"<<x<<endl
#define debug1(x) cout<<#x<<" :"<<x<<" "
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N=2e5+20;
const int MAX=10000007;
inline int read() {
char c=getchar();int x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0',c=getchar();}
return x*f;
}
inline void out(int x) {
if(x>9) out(x/10);
putchar(x%10+'0');
}
int q[N];
const int M = 2000000;
ll pre[M];
ll suf[M];
int n,m;
int main(){
scanf("%d %d",&n,&m);
for(int i=0;i<n;++i){
scanf("%d",&q[i]);
}
for(int i=0;i<(1<<n);++i){
ll sum=0;
for(int j=0;j<n;++j){
if((i)&(1<<j)){
sum^=q[j];
}
}
pre[i]=suf[i]=sum;
}
for(int i=0;i<n;++i){
for(int j=0;j<(1<<n);++j){
if(j&(1<<i)){
pre[j]+=pre[j^(1<<i)];
}
else suf[j]+=suf[j^(1<<i)];
}
}
for(int i=1;i<=m;++i){
int cnt;
int x;
scanf("%d",&cnt);
int st=0;
for(int j=1;j<=cnt;++j){
scanf("%d",&x);
st|=(1<<(x-1));
}
printf("%lld %lld\n",pre[st],suf[st]);
}
return 0;
}