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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现