原题地址:https://ac.nowcoder.com/acm/contest/881/H
核心知识点:
1.对于任何在线性基外的元素,在基内有且只有一种方法使得两者的异或和为0
2.一组数可能会有多种线性基的方案,但是不变的是线性基的数量。
题意:给出一个集合,让求所有子集异或和为0时,子集的大小之和,
思路:首先直接计算子集的大小之和不是很好下手,可以将问题转化为求每个数在子集中出现的次数,然后算n个数产生的总贡献即可。
然后对于这种异或和问题,首选用异或和处理问题。
我们对一开始的n个数字求一次线性基,假如得到一个大小为r的基,那么对于基外的n-r个元素,我们假设选取数字x计算其贡献,选了x之后还剩余n-r-1个元素在基外,那么对于数字x就有 2 n − r − 1 2^{n-r-1} 2n−r−1种方案选择x的子集。
说明一下为什么只用基外的元素来挑选方案。
因为假设你在选中了x之外还挑选了基内的某些元素试图找到更多的方案数,如果你选中的元素中本来就不是使得x的异或和为0的话,那么你加上这个元素就不可能再使得异或为0了,因为由线性基得性质可得,经过消元后得线性基,第i位为1的最多只会有一个数,如果你选择了这个数,就不可能再消掉这位上的1了。
如果你选中的本来就是使x异或为0的元素,那么你会发现最后挑出来的使同一种方案。
所以选择方案只需要用基外的元素即可。
现在挑完了n-r个基外元素,考虑基中的r个元素的贡献。
我们可以枚举基中的元素,对除了选中的数之外的n-1个数求一次线性基,判断选中的元素还能否插入到新求得线性基中,如果能,说明选中了当前元素就不可能会有异或和为0的方案。如果能就会产生 2 n − r − 1 2^{n-r-1} 2n−r−1种方案,理由同上。至于基的数量也是r,参考知识点2.
最后,关于求n-1个元素的线性基,不用每次真的再去求一遍,求出n-r个元素的基,每次重新插入r-1个元素即可。
#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;