P4570 [BJWC2011]元素
题意:给出n个矿石,每个矿石有编号和价值
让我们求出如何融合矿石,才能使最后的价值最大(要满足所选择的矿石的编号异或之后不为0);
思路:根据线性基的性质
- 快速查询一个数是否可以被一堆数异或出来
- 快速查询一堆数可以异或出来的最大/最小值
- 快速查询一堆数可以异或出来的第k大值
- 原数列里的任何一个数都可以通过线性基里的数异或表示出来
- 线性基里任意一个子集的异或和都不为0
- 一个数列可能有多个线性基,但是线性基里数的数量一定唯一,而且是满足性质一的基础上最少的
根据第五第六条性质,因为线性基里面的数量是一定的,也就是说,我们选择的矿石数量是不变的
所以,我们贪心优先选择价值最大的矿石,即:将矿石价值从大到小排序,然后记录下被选择的矿石的价值,最后输出即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=1e3+10; 5 ll p[70]; 6 struct node 7 { 8 ll id; 9 ll sum; 10 }a[maxn]; 11 bool cmp(node x,node y) 12 { 13 return x.sum>y.sum; 14 } 15 int Insert(ll k) { 16 for(ll i=64;i>=0;i--) { 17 if(!(k&(1LL<<i))) continue; 18 if(!p[i]){ 19 p[i]=k; 20 return 1; 21 } 22 k ^= p[i]; 23 } 24 return 0; 25 } 26 int main() 27 { 28 int n; 29 scanf("%d",&n); 30 for(int i=1;i<=n;i++){ 31 scanf("%lld%lld",&a[i].id,&a[i].sum); 32 } 33 sort(a+1,a+1+n,cmp); 34 ll ans=0; 35 for(int i=1;i<=n;i++){ 36 if(Insert(a[i].id)){ 37 ans+=a[i].sum; 38 } 39 } 40 printf("%lld\n",ans); 41 return 0; 42 }