CF1188D Make Equal
https://www.luogu.com.cn/problem/CF1188D
奇妙DP题
https://www.luogu.com.cn/blog/RUI-R/solution-cf1188d
重点是要知道可以分别考虑每一位,按后几位排序后进位的一定是一个前缀,这样就可以优化状态数
code:
#include<bits/stdc++.h>
#define ll long long
#define N 200050
using namespace std;
ll a[N], S;
int f[63][N], id[N], s[N];
int cmp(int x, int y) {
return (a[x] & S) > (a[y] & S);
}
int n;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
sort(a + 1, a + 1 + n);
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 j = 0; j <= 60; j ++) {
S = (1ll << j) - 1;
sort(id + 1, id + 1 + n, cmp);
for(int i = 1; i <= n; i ++) {
int o = (a[id[i]] >> j) & 1;
s[i] = s[i - 1] + o;
}
for(int i = 0; i <= n; i ++) {
int c = (i - s[i]) + (s[n] - s[i]);
int jw = s[i];
f[j + 1][jw] = min(f[j + 1][jw], f[j][i] + c);
c = s[i] + ((n - i) - (s[n] - s[i]));
jw = s[n] + (i - s[i]);
f[j + 1][jw] = min(f[j + 1][jw], f[j][i] + c);
}
}
printf("%lld", f[61][0]);
return 0;
}