题目描述
难度分:1789
输入n(1≤n≤105)、k(0≤k<230)和n个pair(ai,bi),其中0≤ai<230,1≤bi≤109。
从中选择一些pair,满足所有ai的OR
≤k,并且bi 之和越大越好。
输出bi之和的最大值。
输入样例1
3 5
3 3
4 4
2 5
输出样例1
8
输入样例2
3 5
3 3
4 4
2 5
输出样例2
8
输入样例3
3 5
3 3
4 4
2 5
输出样例3
8
算法
按位贪心
这种牵涉到位运算的一般都要拆位,关键要想到拆位的目的,这题我还想了挺长时间的。
考虑选出来的那些数按位或的结果是多少,从最高位开始考虑(本题最高位最大是29)。假设最开始所有数对都是待选状态,考虑到第b位时:
- 如果k在当前位是1,要想选出来的所有元素按位或在这一位是1,就必须要至少保留一个ai在当前位是1的数对,只要现在的待选数对中有这样的数对就全部选上,这样就能使得bi之和最大,直接到下一位b−1上考虑。也可以让这一位是0,通过这样来保证按位或的结果<k,那后面的位就不需要挨个考虑了,因为让当前位为0已经可以满足所有ai的按位或结果≤k,直接把现在所有待选数对中满足ai在当前位为0的bi加起来就是这个策略下的答案。
- 如果k在当前位是0,那么所有bi按位或的结果在这一位也必须是0。把待选数对中满足ai在当前位是1的数对删除,只留下当前位是0的数对到下一位b−1去考虑。
如果b<0,说明从最低位已经考虑完了。把当前的待选数对中所有bi加起来就是当前策略下的答案。维护所有策略下bi累加和的最大值即可。
复杂度分析
时间复杂度
遍历30个位,在极端情况下每个位需要进行O(n)级别的数组遍历。因此,时间复杂度为O(nlog2U)。
空间复杂度
递归实现按位考虑的过程,那么空间复杂度就是递归深度,为O(30)。输入的n个数对不计入空间复杂度,因此额外空间复杂度为O(log2U),其中U为所有数对中ai的最大值。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, k;
LL ans;
bool check(int b, vector<array<int, 2>>& tup) {
for(int i = 0; i < tup.size(); i++) {
if(tup[i][0]>>b&1) return true;
}
return false;
}
void dfs(int b, vector<array<int, 2>>& tup) {
if(b == -1) {
LL sum = 0;
for(int i = 0; i < tup.size(); i++) {
sum += tup[i][1];
}
ans = max(ans, sum);
return;
}
int bit = k>>b&1;
if(bit == 1) {
if(check(b, tup)) {
// 当前位保持1
dfs(b - 1, tup);
}
// 当前位是0,那直接所有数都选上
LL sum = 0;
for(int i = 0; i < tup.size(); i++) {
if(tup[i][0]>>b&1) continue;
sum += tup[i][1];
}
ans = max(ans, sum);
}else {
// 当前位是0,只能选择剩余的数中这一位是0的数
vector<array<int, 2>> nxt;
for(auto&pir: tup) {
if(pir[0]>>b&1) continue;
nxt.push_back(pir);
}
dfs(b - 1, nxt);
}
}
int main() {
scanf("%d%d", &n, &k);
vector<array<int, 2>> tup;
for(int i = 1; i <= n; i++) {
int a, b;
scanf("%d%d", &a, &b);
tup.push_back({a, b});
}
ans = 0;
dfs(29, tup);
printf("%lld\n", ans);
return 0;
}