B3618 寻找团伙
我来发个位运算枚举子集。
题意
给一个 $n$ 个数的集合,找出一个子集使其异或和最大,求出这个异或和。
思路
$n$ 很小,考虑直接 $2^n$ 枚举。
定义 $p_i$ 为一个人能力的权重和,首先要算出 $p_i$。
只需要把第 $i$ 个人所有能力的权重按位或起来就可以了。
需要注意的是,如果要用 1 << k - x 的方式来算 $2^{k - x}$,那么必须写成 1ull << k - x,
因为 1 << k - x 里的 1 是 int 型的,把 int 型的数左移 $60$ 位是未定义行为。
这样这个集合就求出来了,接下来我们要枚举子集。
我们用一个二进制数来表示每个数选不选,
具体地,s & (1 << i) == 1 代表选择第 $i$ 个数。
现在需要枚举所有的 $s$,也就是所有不多于 $n$ 位的二进制数。
可以发现,需要枚举 $[0,2^n)$ 之内的数。
对于每个 $s$,求出它所代表的的子集的异或和,取最大值即可。
代码
#include <cstdio>
unsigned long long p[50], s, u, ans, sum;int n, k;
int main()
{
scanf("%d%d", &n, &k);u = 1 << n;
for(int i = 0, t, x;i < n;++i)
{
scanf("%d", &t);
while(t--) scanf("%d", &x), p[i] |= 1ull << k - x; //注意是 1ull
}
for(;s < u;++s) //枚举 s
{
sum = 0;
for(int i = 0;i < n;++i)
if(s & (1 << i)) sum ^= p[i]; //算出 s 代表的子集的异或和
if(sum > ans) ans = sum; //更新最大值
}
printf("%lld", ans);
return 0;
}

浙公网安备 33010602011771号