给定长度为
的
数组,一共
次询问:每次询问给定一个长度
的
数组的一个子集。
定义:集合的权值为该集合内所有元素的异或值
定义:超集为包含该集合的其他所有长度为
的
数组的子集
求该子集的所有子集的权值之和 以及 该子集的所有超集的权值之和
例如题目样例:
数组为
第
次查询为长度为
的子集
(这是下标),所以实际为
该集合的所有子集为
,
,
;对应的权值为
,
,
;所以所有子集的权值为
该集合的所有超集为
,
;对应的权值为
,
;所以所有超集 的权值为
解决这道题目使用的前缀和的思想
设
(其中
的二进制表示状态,即构成的集合)表示其包含所有子集的权值之和
设
表示其包含所有超集的权值之和
当前的状态为
,
是状态
二进制下第
位,如果满足
,那么有:
这就是子集更新与超集更新的式子,因为是每一位考虑的,所以不会遗漏与重复!
总代码:
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
#define HelloWorld IOS;
const int N = 21;
int pre[1 << N], fpre[1 << N];
signed main(){
HelloWorld;
int n, q; cin >> n >> q;
vector<int> a(n);
for(int i = 0; i < n; i ++) cin >> a[i];
for(int i = 0; i < (1 << n); i ++){
int res = 0;
for(int j = 0; j < n; j ++){
if((i >> j) & 1) res ^= a[j];
}
pre[i] = res, fpre[i] = res;
}
for(int i = 0; i < n; i ++){
for(int j = 0; j < (1 << n); j ++){
if((j >> i) & 1) pre[j] += pre[j - (1 << i)];
else fpre[j] += fpre[j + (1 << i)];
}
}
while(q --){
int k; cin >> k;
int x = 0;
for(int i = 1; i <= k; i ++){
int q; cin >> q;
q --;
x += (1 << q);
}
cout << pre[x] << " " << fpre[x] << endl;
}
return 0;
}



京公网安备 11010502036488号