洛谷 B3618 寻找团伙

# 暴力or状态压缩or线性基

题意:给定 n 个员工,以及员工的能力总数 k , 每个员工 i 有 c_{i} 种能力,第 j 种能力 x_{j} 可以提供 2^{k-x_{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))    # 二进制转整数输出

(转自阮行止的博客)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值