我们记以二进制第 i 位为最高位且这一位为 1 时,其所有小于他的位之和一定小于这一位的值 枚举答案 x 的第 1−60 位为最高位,,我们选择第 i 位为最高位时,由 结论A,我们一定会选择所有第 i位为 0的所有值,一定不选第 i 位为 1 的值,否则一定不优。 这时记选择的这个集合为 S,考虑对于集合 S 枚举小于 i 的所有位,如果选择这一位对数组和的贡献大于 0,那么先选上,等选择完所有贡献大于 0 的位置后,再贪心的删除多余的位置。 现在我们选中了所有可以产生贡献的位置,这是选择第 i 位作为最高位时我们可以得到的最大贡献,现在我们考虑删除多余的一些贡献来使答案 x 最小。可知,删掉高位的贡献比删掉所有低位的贡献之和还要更高,所以从高到低枚举每一位,如果删掉这一位数组之和依然大于等于 y,那么删掉这一位是一定最优的做法
void solve()
{
int n, y;
cin >> n >> y;
vi<int> a(n + 1);
i64 sum = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
}
i64 ans = (1ll << 62);
for (int i = 62; i >= 0; i--)
{
vi<int> pos;
vi<int> pre(70);
int now = 0;
for (int j = 1; j <= n; j++)
{
if ((a[j] >> i) % 2 == 0)
{
pos.push_back(a[j]);
}
}
for (int j = 0; j <= i; j++)
{
int num = 0;
for (auto v : pos)
{
if ((v >> j) & 1)
num -= (1ll << j);
else
num += (1ll << j);
}
pre[j + 1] = pre[j];
if (num > 0)
{
pre[j + 1] += num;
}
}
int ns = sum;
for (int j = i; j >= 0; j--)
{
if (ns + pre[j] >= y)
{
continue;
}
else
{
now += (1ll << j);
ns += pre[j + 1] - pre[j];
}
}
if (pre[i + 1] + sum >= y)
ans = min(ans, now);
}
cout << ans << endl;
}