CF1188D Make Equal
假设 a 数组有序,记 cnt(x) 表示 x 的二进制表示中 1 的个数。
那么我们就是要找一个 x 使得 ∑ni=1cnt(x+an−ai) 最小。下面令 ai←an−ai。
我们考虑 x+ai 的第 k 位。这一位的决策与下面的东西有关:
- x 这一位填不填 1(要决策的东西)
- ai 这一位是不是 1
- 前一位的进位情况
棘手的是第三种情况,如果暴力记录,有 2n 种情况。但是注意到,由于 x 是相同的,那么 ai 前 k−1 位越大越容易进位。如果将所有 ai 按照 aimod2k 的大小从大到小排序,则最后进位的一定是一段前缀,那么就可以 DP 了。
设 fi,j 表示前 i 位,有 j 个数进位。转移有如下四种情况:
- x+ak 在第 i 位没有进位,ak 的 i+1 位为 1。
- x+ak 在第 i 位没有进位,ak 的 i+1 位为 0。
- x+ak 在第 i 位进位,ak 的 i+1 位为 1。
- x+ak 在第 i 位进位,ak 的 i+1 位为 0。
枚举 x 的第 i+1 位:
- 该位为 1,第一、三、四种会进位,第二、三类 i+1 位为 1。
- 该位为 0,第三类会进位,第一、四类 i+1 位为 1。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100005;
int n;
ll a[N];
int f[62][N];
int id[N];
int K;
int sum[2][N];
bool cmp(int x, int y) {
return (a[x] & ((1ll << K) - 1)) < (a[y] & ((1ll << K) - 1));
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i) a[i] = a[n] - a[i], id[i] = i;
memset(f, 0x3f, sizeof f); f[0][0] = 0;
for (int k = 0; k <= 60; ++k) {
K = k;
sort(id + 1, id + n + 1, cmp);
for (int i = 1; i <= n; ++i) {
sum[0][i] = sum[0][i - 1],
sum[1][i] = sum[1][i - 1];
++sum[a[id[i]] >> k & 1][i];
}
for (int i = 0; i <= n; ++i) {
int x, y;
x = sum[1][n - i] + sum[0][n] - sum[0][n - i], y = sum[1][n] - sum[1][n - i];
f[k + 1][y] = min(f[k + 1][y], f[k][i] + x);
x = sum[0][n - i] + sum[1][n] - sum[1][n - i], y = n - sum[0][n - i];
f[k + 1][y] = min(f[k + 1][y], f[k][i] + x);
}
}
printf("%d", f[61][0]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通