如何用格雷码优化枚举子集
最近在给小朋友们备“位运算”这一节的课,在洛谷搜罗题目的时候发现这么一个非同寻常的题目。
目前我们看到,这题目前只能卡常完成,也就是枚举每个 __builtin_ffs 来快速敲掉
那么,有没有一种方法枚举子集,使得我们每次可以只改变一个数呢?
有的,格雷码就是例子。
所以我们使用格雷码维护所有子集,就可以保证每次只需要修改一个数,不需要使用前缀和技巧。但是递归常数非常感人,因此好像相比于维护当前要改的数位,还是直接 __builtin_ctz 快一点。
#include<bits/stdc++.h>
using namespace std;
int n;
unsigned long long a[32],ans,total;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%llu",&a[i]);
int S=0;
for(int stt=1;stt<(1<<n);stt++)
//stt记录哪些数翻过哪些没翻过
{
int lbt=(stt)&(-stt),bit=__builtin_ctz(lbt);
//修改数位的位值,以及哪一位要修改
total^=a[bit];
S^=lbt;//S以格雷码顺序枚举
ans^=total*S;
}
printf("%llu\n",ans);
}