CF1188D Make Equal 题解

CF1188D Make Equal

询问把一个长为 \(n\) 的数组变成同一个数的最小操作次数,一次操作是给 \(a_i \leftarrow a_i+2^k\)。

先给 \(a\) 排序,则令最终所有数为 \(x+a_n\),我们就要最小化 \(\sum_{i=1}^n \mathrm{popcount}(x+a_n-a_i)\),直接令 \(a_i=a_n-a_i\)。

要最小化 \(\sum \mathrm{popcount}(x+a_i)\),我们就按位从低到高考虑,我们需要知道 \(a_i\) 第 \(j\) 位的值,\(x\) 第 \(j\) 位的值,以及在 \(j-1\) 位进位了几次。其中 \(a_i\) 我们是知道的,\(x\) 是需要我们分两种情况决策的,那关键就在于上一位进位了几次。

做一个巧妙的考量,在第 \(j\) 位进位的次数,肯定是在 \(\bmod 2^j\) 下最大的那几个 \(a_i\) 进位。那我们就每一位都把 \(a\) 按照 \(\bmod 2^j\) 从小到大排序。记 \(sum_{i,0/1}\) 表示排序后的数组这一位上 \(0/1\) 数量的前缀和,\(f_{i,j}\) 表示完成了 \(0~i-1\) 位,第 \(i-1\) 位向第 \(i\) 位进位了 \(j\) 次时最小的 \(\mathrm{popcount}\)。

分两种情况考虑转移:

1.当 \(x\) 这一位为 \(0\) 时,\(x\) 对原数组没有影响,新增的 \(1\) 个数 \(cst\) 为原本不进位的数里 \(1\) 的个数 \(sum_{n-j,1}\),加上原本进位的数里为 \(0\) 的个数 \(sum_{n,0}-sum_{n-j,0}\),因为原本是 \(0\) 进位就变成 \(1\) 了。此时新的进位数 \(cry\) 为原本进位的数里为 \(1\) 的数个数。

2.当 \(x\) 这一位为 \(1\) 时,\(x\) 对这一位的数组都会再加 \(1\)。\(cst\) 等于原本不进位且是 \(0\) 的数个数加上原本进位且是 \(1\) 的数个数,因为 \(1+1+1\) 还是 \(1\)。\(cry\) 等于除了原本是 \(0\) 不进位里的数的所有数。

时间复杂度 \(O(n \log^2 n)\)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n;
const int N = 1e5+10,V=61;
int a[N],id[N],cur;
int f[V+4][N],sum[N][2];
bool cmp(int x,int y){
    return (a[x]&((1ll<<cur)-1))<(a[y]&((1ll<<cur)-1));
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        id[i]=i;
    }
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)a[i]=a[n]-a[i];
    for(int i=0;i<V;i++){
        memset(sum,0,sizeof(sum));
        cur=i;
        sort(id+1,id+1+n,cmp);
        for(int j=1;j<=n;j++){
            sum[j][0]=sum[j-1][0];sum[j][1]=sum[j-1][1];
            sum[j][(a[id[j]]>>i)&1]++;
        }
        for(int j=0;j<=n;j++){
            int cst=sum[n-j][1]+(sum[n][0]-sum[n-j][0]);
            int cry=sum[n][1]-sum[n-j][1];
            f[i+1][cry]=min(f[i+1][cry],f[i][j]+cst);
            cst=sum[n][1]-sum[n-j][1]+sum[n-j][0];
            cry=n-sum[n-j][0];
            f[i+1][cry]=min(f[i+1][cry],f[i][j]+cst);
        }
    }
    cout<<f[V][0];
    return 0;
}
posted @ 2026-10-02 22:20  farenara_sayowell  阅读(8)  评论(0)    收藏  举报