题目大意:
有许多矿石,矿石有编号和价值两种属性,我们要求一个集合,使得该集合任意子集编号异或和不为0,并且要求价值和尽量大。
题目分析:
先科普线性基。
线性基是我们构造出一个集合,这个集合中的所有元素能相互异或能得到原集合中任意子集异抑或和。
当然我们构造出的线性基是具有一些优秀性质的。
线性基中的元素只有二进制位数个。
其中第i个元素ins[i]代表用所有数能异或出的最高位为第i位的数。
构造方法:
对于一个数,从高往低对于每一个二进制位判断。
如果这一位是1:
如果线性基中最高位为这一位的数为0,那就把这个数加进线性基,并跳出扫描,否则就把这个数异或上线性基中这一位的元素(相当于把这一位消去),然后比较下一位。
如果这一位是0:
比较下一位。
这道题可以贪心的把价值按照从大到小排序,然后扫描一遍,如果当前的矿石编号不能被前面所有的矿石异或出来的话,就选进来。
这样的话在构造线性基的时候就可以直接判断出来了。
构造线性基的时候,如果这个数不能加进线性基里,即最后被异或成0,那就说明这个数能被前面的数异或出来,就不能加进答案。
代码如下:
#include <cstdio>
#include <algorithm>
#define N 1005
using namespace std;
typedef long long LL;
struct Node{
int x;
LL val;
bool operator < (const Node &c) const {return x>c.x;}
}a[N];
LL ins[66],ans;
int n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%d",&a[i].val,&a[i].x);
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
{
for(int k=65;k>=0;k--)
{
if(a[i].val>>k)
{
if(!ins[k]){ins[k]=a[i].val; break;}
a[i].val^=ins[k];
}
}
if(a[i].val) ans+=a[i].x;
}
printf("%lld\n",ans);
return 0;
}