Maximum And Queries (hard version)
效率异常低下.
\(\tt{Solution}\)
先考虑一下 easy version.
观察到 \(nq \le 10^5\), 所以对于每一次询问, 我们可以按位来进行贪心.
从 \(2^{59}\) 一直遍历到 \(2^0\), 考虑当前位 \(2^i\) 是否能够出现.
我们可以直接暴力模拟所有 \(n\) 个数的第 \(i\) 位全部变成 \(1\) 的代价之和, 并与目前能够操作次数进行比较即可.
具体地, 我们进行分类讨论:
- 如果当前位已经是 \(1\), 则代价为 \(0\).
- 如果当前位不是 \(1\), 并且 \(a_j < 2^i\), 则 \(a_j = 2^i\), 代价为 \(2^i - a_j\).
- 否则, 将第 \(i\) 位变成 \(1\), 更低位全部变成 \(0\) 即可.
再来看 hard version.
因为 \(1 \le n, q \le 10^6\), 每次询问 \(\mathcal{O}(n)\) 暴力模拟肯定就行不通了.
延续 easy version 的贪心, 需要优化的是 \(\mathcal{O}(n)\) 枚举每个数的过程,
注意到一个性质: 如果一个数更新到第 \(i\) 位是 \(1\) 后, 那么第 \(0 \sim i - 1\) 位都会是 \(0\). 换句话说, 再往后遍历的时候, 如果你想使得答案有第 \(j\) 位, 那么该数一定会贡献 \(2^j\) 次操作.
由此可以想到根据答案的最高位 \(2^w\) 进行分讨, 如果 \(w \ge 20\), 那么答案就直接是 \(2^w + \lfloor \frac{left}{n} \rfloor\), 其中 \(left\) 为剩余操作次数.
如果 \(w < 20\), 那么对于每种情况, 我们分别考虑代价, 设答案为 \(ans\).
- 更高位已经被更新过, 即 \(a_j\ \& \ ans < ans\), 代价为 \(2^i\), 数量在贪心的过程中顺便统计即可.
- 之前未被更新过, 即 \(ans\) 是它高位的子集, 假设这类数共有 \(f_i\) 个, 总和为 \(g_i\), 那么代价即为 \(2^i \times f_i - g_i\).
如何求出 \(f, g\) 数组? 发现符合要求的数满足「某一位必须不存在, 在这一位之前的若干位必须存在」, 预处理时可以枚举位 \(i\), 对于 \(i\) 左侧等价于限制了一段前缀, 并且限制为一个超集状物, 于是对于每一段二进制前缀做一个高维后缀和即可.
时间复杂度 \(\mathcal{O}(n \log^2 V)\).
#include "iostream"
using namespace std;
constexpr int N = 1e6 + 10, M = 20;
#define int long long
int n, q, a[N], mx = 0;
int g[M][1 << M], f[M][1 << M];
void init() {
cin >> n >> q;
for (int i = 1; i <= n; ++i)
cin >> a[i], mx += (1 << 20) - a[i];
for (int i = 19; ~i; --i) {
for (int j = 1; j <= n; ++j)
if (!(a[j] >> i & 1))
++f[i][a[j]], g[i][a[j]] += a[j] & ((1 << i) - 1);
for (int j = 0; j <= 19; ++j)
for (int k = (1 << 20) - 1; k >= (1 << j); --k) if (k >> j & 1)
f[i][k ^ (1 << j)] += f[i][k], g[i][k ^ (1 << j)] += g[i][k];
}
}
void calculate() {
while (q--) {
int k, ans = 0;
cin >> k;
if (k >= mx) {
cout << (1 << 20) + (k - mx) / n << '\n';
continue;
}
for (int i = 19, c = 0; ~i; --i) {
int cst = c * (1 << i) - g[i][ans] + (1 << i) * f[i][ans];
if (k >= cst)
k -= cst, c += f[i][ans], ans |= 1 << i;
}
cout << ans << '\n';
}
}
void solve() {
init();
calculate();
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}

浙公网安备 33010602011771号