题目大意:有N个非负数A1 A2… AN,要找到最大的k使得
f
(
k
)
=
∑
i
=
1
N
A
i
x
o
r
k
≤
M
f(k) = \sum_{i=1}^{N}A_i\space xor\space k \le M
f(k)=i=1∑NAi xor k≤M
- 1 ≤ N ≤ 1000.
- 0 ≤ M ≤ 1015.
- 0 ≤ Ai ≤ 1015, for all i.
将二进制数的位从低往高编号,依次为bit 0, 1… 。1015 < 250,所以任意Ai的bit 50及更高都是0,k必须< 250
设nums[i] = bit i为1的Aj的个数,k的bit i为Ki,如果Ki = 1,那bit i贡献(N - nums[i]) * 2i;如果Ki = 0,那bit i贡献nums[i] * 2i。f(k)就是50位的贡献总和。
问题变成,现在有一个2×50的非负数矩阵Mat,行号0-1,列号0-49。令sum=0,k=0,然后每列都二选一,假设列j选择了Mat[i][j],sum加上该值,并把k的bit j设为i。求出最大的k使得sum <= M。
用贪心法解决这个问题。要使得k尽量大,那高位要尽可能放1。我们从高往低看,即按列号递减的顺序看。当到达列j,先尝试放1,如果这里选1,且更低的列都选两个里面更小的那个,加起来得到的sum <= M,说明可以选1达到最优。如果不能选1,接着尝试0,还不能的话说明不存在符合的k。
#include <iostream>
#include <vector>
#include <unordered_map>
#include <string>
#include <algorithm>
using namespace std;
using ll = long long;
const ll MAXM = 1e15; // 1e15 < 2^50
const int B = 50;
class Solution {
public:
void solve() {
int N; // 1 <= N <= 1000
ll M; // 0 <= M <= MAXM
cin >> N >> M;
vector<int> nums(B); // nums[i] 第i位为1的Aj的个数
for(int i = 0; i < N; i++) {
ll x; // 0 <= x <= MAXM
cin >> x;
for(int j = 0; j < B; j++) {
if(x & (1ll << j)) nums[j]++;
}
}
vector<vector<ll>> values(B, vector<ll>(2));
for(int i = 0; i < B; i++) {
ll coef = 1ll << i;
values[i][1] = coef * (N - nums[i]);
values[i][0] = coef * nums[i];
}
vector<ll> minsum(B);
for(ll i = 0, x=0; i < B; i++) {
x = minsum[i] = x + min(values[i][0], values[i][1]);
}
ll res = 0, sum = 0;
for(int i = B-1; i >= 0; i--) {
bool ok = false;
for(int j = 1; j >= 0; j--) {
if((i == 0 && sum + values[i][j] <= M) || (i > 0) && (sum + values[i][j] + minsum[i-1] <= M)) {
sum += values[i][j];
if(j == 1) res |= (1ll << i);
ok = true;
break;
}
}
if(!ok) {
res = -1; break;
}
}
cout << res << endl;
}
};
int main() {
int T;
cin >> T;
auto sln = new Solution();
for (int i = 1; i <= T; i++)
{
cout << "Case #" << i << ": ";
sln->solve();
}
return 0;
}