B - Plus and AND
标签:贪心 二进制 排序
题意:
- 给出一个长度为n的数组a,和最多操作次数m
- 每次操作可任意选择数组中一个数加一
- 最后从数组中选择k个数,使其与运算结果最大并输出结果
- 1≤K≤N≤2×10
- 0≤M<2^30
- 0≤Ai<2^30
思路:
因为 2^n > 2^(n-1) + 2^(n-2) + …… + 1
所以我们对位数从高到底进行贪心
若a[j]在二进制中第i位为1,则不需要消耗操作次数
否则要需要消耗(1ll << i) - (a[j] % (1ll << i))次
选择前k小消耗次数,若和小于等于m则表示能拿下这位
思路在比赛时是想到了,但可惜没想到怎么实现
代码:
#define fst std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout << std::fixed << std::setprecision(20)
#define le "\n"
#define ll long long
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+50;
const int mod=1e9+7;
int main()
{
fst;
ll n, m, k;
cin>>n>>m>>k;
vector <ll> a(n);
for(int i=0;i<n;i++) cin >> a[i];
vector <ll> num(n, 0), tmp(n);//都用来存储操作次数,tmp数组临时存放操作次数,用来判断可不可行
ll ans = 0;
for(int i=31;i>=0;i--){
for(int j=0;j<n;j++){
tmp[j] = num[j];
if(a[j] >> i & 1) tmp[j] += 0;
else tmp[j] += (1ll << i) - (a[j] % (1ll << i));
}
//sort(tmp.begin(), tmp.end());这个也行
nth_element(tmp.begin(), tmp.begin() + k, tmp.end());//使前k小操作次数位于前k位
ll sum = accumulate(tmp.begin(), tmp.begin() + k, 0LL);
if(sum <= m){
ans |= 1ll << i;
for(int j=0;j<n;j++){
if(a[j] >> i & 1) num[j] = num[j];
else{
num[j] += (1ll << i) - (a[j] % (1ll << i));
a[j] = 1LL<<i;
}
}
}
}
cout << ans << le;
return 0;
}