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;
}
posted @ 2022-03-22 19:25  Jijidawang  阅读(39)  评论(0)    收藏  举报  来源