CF1188D Make Equal

假设 a 数组有序,记 cnt(x) 表示 x 的二进制表示中 1 的个数。
那么我们就是要找一个 x 使得 ni=1cnt(x+anai) 最小。下面令 aianai
我们考虑 x+ai 的第 k 位。这一位的决策与下面的东西有关:

  • x 这一位填不填 1(要决策的东西)
  • ai 这一位是不是 1
  • 前一位的进位情况

棘手的是第三种情况,如果暴力记录,有 2n 种情况。但是注意到,由于 x 是相同的,那么 aik1 位越大越容易进位。如果将所有 ai 按照 aimod2k 的大小从大到小排序,则最后进位的一定是一段前缀,那么就可以 DP 了。
fi,j 表示前 i 位,有 j 个数进位。转移有如下四种情况:

  • x+ak 在第 i 位没有进位,aki+1 位为 1
  • x+ak 在第 i 位没有进位,aki+1 位为 0
  • x+ak 在第 i 位进位,aki+1 位为 1
  • x+ak 在第 i 位进位,aki+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;
}
posted @   Kobe303  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示