# 暴力or状态压缩or线性基
题意:给定 n 个员工,以及员工的能力总数 k , 每个员工 i 有 种能力,第 j 种能力 可以提供 的分值。从 n 个员工中选出 m 个员工时,如果这 m 个员工有偶数个人拥有某个能力,那么这个能力就不能提供分值,反之,如果是奇数个人拥有该能力,那么该能力就能提供分值,求任选几个员工后最大的分值。
解法:对于每种能力,需要记录当前能力选了几次吗?其实是不需要的,用抑或运算可以很快地记录选了某个员工后当前的分值,因为对于任意一种能力,只有提供或不提供分值两种情况,选下一个员工后,该员工拥有的能力中,原来提供分值的会不提供,不提供的会提供分值,这时用抑或就可以快速统计了,因为一个数抑或上另一个数以后,结果里另一个数二进制中为 1 的位置都会改变,而不是的则不变。
## 线性基
如果存在两个集合 S,B ,且 S 的任意子集的抑或和都能通过 B 的子集以抑或和的形式表示时,则称∣B∣ 最小的一个 B 是 S 的线性基。
线性基的性质:线性基中任意两个元素的二进制最高位不同;线性基的子集抑或和唯一;线性基中任意元素的异或和不为 0 。
为了满足上面的三个性质,在线性基插入元素 x 时,从高位向低位扫,如果 x 二进制下该位为 1,且不存在最高位为该位的线性基元素,将其插入线性基。如果该位为 1,但已经存在元素的最高位为该位,将 x 异或上该元素,继续向后扫。
线性基求异或最值问题时,只需要进行贪心就可以,求最大值从高位向低位扫,如果抑或上线性基中对应元素以后更大,则进行抑或。求最小值时,只用取线性基中最小的元素就可以了。
(转自ZYingy的博客)
import numpy as np
from functools import reduce
n, k = map(int, input().split())
s = np.zeros([k, k], dtype=int)
for x in range(n):
r = list(map(int, input().split()))[1:]
p = np.array([1 if i+1 in r else 0 for i in range(k)])
for pos in range(k): # 线性基
if p[pos] == 1:
if s[pos, pos] == 1:
p ^= s[pos]
else:
s[pos] = p
break
res = np.zeros([k], dtype=int)
for pos in range(k):
if res[pos] == 0 and s[pos, pos] == 1: # 贪心
res ^= s[pos]
print(reduce(lambda x,y: x*2+y, res)) # 二进制转整数输出
(转自阮行止的博客)