这个题从题面到解法都是很新颖的
先跳出前缀和这个框架,想想这个题在干什么
N = 20
M = 1e5
数据范围很多时候会提示数组怎么开,以及算法复杂度是多少
Q1:这个复杂度,提醒什么?
2表示这N个物品的某一个,取,还是不取
Q2:20维度的数组开不下,咋办?
开成一维的,用二进制位压缩
之后才会想到前缀和:因为要维护子集合与超集合
把 N 个物品理解为向量,先想二维的情况
F[A][B] = v[A][B]
F[A][B] += F[0][B] + F[A][0]
推广到 N 维:
F[A][B][C][D][E]... = v[A][B][C][D][E]...
F[A][B][C][D][E]... += v[A][0][0][0][0]... + ...
写成容斥,在维度上是不好扩展的。而写成空间向量的理解,是很好扩展的
加上二进制位运算
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 20;
const int maxm = 1e5 + 10;
ll pre[(1<<maxn) + 20], suf[(1<<maxn) + 20], v[(1<<maxn) + 20];
int n, m, k, tmp, x;
int a[maxm];
int main(){
//freopen("input.txt", "r", stdin);
scanf("%d%d", &n, &m);
memset(pre, 0, sizeof(pre));
memset(suf, 0, sizeof(suf));
memset(v, 0, sizeof(v));
for(k = 0; k < n; k++)
scanf("%d", &a[k]);
for(int i = 0; i < (1 << n); i++){
for(int j = 0; j < n; j++)
if (i & (1 << j))
v[i] = v[i] ^ a[j];
pre[i] = suf[i] = v[i];
}
for(int j = 0; j < n; j++)
for(int i = 0; i < (1 << n); i++)
if (i & (1 << j))
pre[i] += pre[i ^ (1 << j)];
else
suf[i] += suf[i ^ (1 << j)];
while(m--){
scanf("%d", &k);
x = 0;
while(k--){
scanf("%d", &tmp);
x += (1 << (tmp - 1));
}
printf("%lld %lld\n", pre[x], suf[x]);
}
return 0;
}



京公网安备 11010502036488号