题目描述
相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术。那时人们就认识到,一个法杖的法力取决于使用的矿石。
一般地,矿石越多则法力越强,但物极必反:有时,人们为了获取更强的法力而使用了很多矿石,却在炼制过程中发现魔法矿石全部消失了,从而无法炼制出法杖,这个现象被称为“魔法抵消” 。特别地,如果在炼制过程中使用超过一块同一种矿石,那么一定会发生“魔法抵消”。后来,随着人们认知水平的提高,这个现象得到了很好的解释。经过了大量的实验后,著名法师 Dmitri 发现:如果给现在发现的每一种矿石进行合理的编号(编号为正整数,称为该矿石的元素序号),那么,一个矿石组合会产生“魔法抵消”当且仅当存在一个非空子集,那些矿石的元素序号按位异或起来为零(如果你不清楚什么是异或,请参见下一页的名词解释 )。
例如,使用两个同样的矿石必将发生“魔法抵消”,因为这两种矿石的元素序号相同,异或起来为零。并且人们有了测定魔力的有效途径,已经知道了:合成出来的法杖的魔力等于每一种矿石的法力之和。人们已经测定了现今发现的所有矿石的法力值,并且通过实验推算出每一种矿石的元素序号。
现在,给定你以上的矿石信息,请你来计算一下当时可以炼制出的法杖最多有多大的魔力。
输入格式
第一行包含一个正整数 N,表示矿石的种类数。
接下来 N 行,每行两个正整数Numberi 和 Magici,表示这种矿石的元素序号和魔力值。
输出格式
仅包含一行,一个整数代表最大的魔力值。
题解
第一眼看到这道题,首先想到贪心,因为所有元素中能促成“魔法抵消”的非空子集是固定的,不固定的是这些子集中被选择的矿石,把所有矿石按法力值从大到小进行排序,遍历数组进行check操作,如果该矿石被遍历时加入它不会造成“魔法抵消”则加入矿石,否则遍历下一个矿石。
现在的问题就变成了:如何快速判断矿石能否被加入。
考虑到Number的值很大,不能暴力,想到能否用二进制位数来简化操作,于是发现可以用线性基处理数据。
这样问题就都解决了,下面是代码解释和代码。
1. 数据结构和输入
- 定义了一个
struct St
,其中有两个属性:Number
和Magic
。Number
是我们要处理的数字,而Magic
是这个数字的权重。 - 使用
bool operator<
来实现排序的自定义规则,即根据Magic
值进行降序排序。排序是为了在最大化魔法值时优先考虑较大的权重。
2. 主逻辑
1) 输入数据
- 从标准输入读取数字的数量
N
,然后读取N
个Number
和Magic
值,并保存在st
数组中。
2) 排序
- 对结构体数组
st
按照Magic
值进行降序排序。这保证了我们在后续处理中能优先考虑魔法值大的数字。
3) 检查线性无关性
- 使用
check
函数来决定是否可以将当前的Number
添加到我们的线性基中。这个函数尝试将数字与已有的基进行线性组合。- 通过遍历从高位到低位的二进制位,使用
dp[i]
数组来存储目前已经建立的线性基。 - 如果在某一位没有已有的基元素(即
dp[i]
为 0),那么就把当前数字x
加入基,并返回true
。 - 若在当前位已经有了基元素,那么就用当前数字与这个基元素异或,尝试降低数字的复杂性。
- 通过遍历从高位到低位的二进制位,使用
4) 计算总魔法值
- 遍历排序后的结构体数组
st
:- 对于每个数字,调用
check
函数,若它可以成功加入线性基,则累加它的魔法值到ans
中,表示我们成功选择了这个数字。
- 对于每个数字,调用
3) 输出结果
- 最后,打印总的魔法值
ans
。
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
ll dp[64];
struct St{
ll Number,Magic;
bool operator<(const St &t)const{
return Magic>t.Magic;
}
}st[1006];
bool check(ll x){
for(int i=61;i>=0;i--){
if(x>>i){
if(dp[i+1]==0){
dp[i+1]=x;
return true;
}
else {
x^=dp[i+1];
}
}
}
return false;
}
int main(){
int N;
cin>>N;
for(int i=1;i<=N;i++){
cin>>st[i].Number>>st[i].Magic;
}
sort(st+1,st+N+1);
ll ans=0;
for(int i=1;i<=N;i++){
if(check(st[i].Number))ans+=st[i].Magic;
}
cout<<ans;
return 0;
}