题解 CF1188D 【Make Equal】
题意:给定一个序列,每次可以选一个数并将其加上2的幂次方,求使所有数相等的最少步数。
原题
真心感谢
可能是我理解能力堪忧,感觉其它题解讲的有些简略。
有一个朴素而naive的想法就是算出每个数与最大值的差值,算一下二进制下有几个1就好了。令
可惜看一眼样例就知道假掉了。因为可以对最大值进行操作,从而使答案变优。
那么,如果令最大值增加
为了方便,令
那么我们一位一位的来尝试DP。
对于每一位,要考虑
其中
乍一看,好哇,
但是仔细一想,似乎越大的越容易进位。这里的“越大的”指的是当前考虑的位数以下的大小。
那只要记录上一位有几个进位的就好了,因为我们知道进位的一定是按当前考虑的位数以下的大小降序排列后的前几位。
那么状态就只有
令
接着,考虑排序后如何转移:(具体)
可以发现我们不关心这个数到底是多少,只关心1的数量和进位的数量
如果钦定上次前
令
1:上次进位,这一位是1,有
那么,开始决策:
#include <cstdio>
#include <algorithm>
#include <vector>
typedef long long ll;
const int maxn=1e5+5,lim=62,maxm=65;const ll inf=1e18;
template<typename T> inline T min(T a,T b){
return a<b?a:b;
}
int n;ll a[maxn],mod,dp[maxm][maxn];
// inline bool Comp(ll a,ll b){
// return (a%mod)>(b%mod);
// }
inline bool Comp(ll a,ll b){
return a>b;
}
void radix_sort(){
std::vector<ll> bit[2];
for(int i=1;i<=n;i++){
bit[(a[i]&(1ll<<(mod-1)))>0ll].push_back(a[i]);
}
int cnt=0;
for(int i=0;i<bit[1].size();i++) a[++cnt]=bit[1][i];
for(int i=0;i<bit[0].size();i++) a[++cnt]=bit[0][i];
}
int main(){
scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
std::sort(a+1,a+n+1,Comp);
for(int i=n;i>=1;i--) a[i]=a[1]-a[i];
for(int i=0;i<=lim;i++) for(int j=0;j<=n;j++) dp[i][j]=inf;
dp[0][0]=0;
for(int i=0;i<lim;i++){
mod=i;if(i) radix_sort();//如果是在第0位,不需要排序
ll cnt=0,tot=0;mod=1ll<<i;
for(int j=1;j<=n;j++){
cnt+=a[j]&mod?1:0;
}
for(int j=0;j<=n;j++){
tot+=a[j]&mod?1:0;
//x[i]->1,1~j:1->1,+1,0->0,+1 j+1~n:1->0,+1,0->1,+0
dp[i+1][cnt-tot+j]=min(dp[i+1][cnt-tot+j],dp[i][j]+tot+(n-j)-(cnt-tot));
//x[i]->0,1~j:1->0,+1,0->1,+0 j+1~n:1->1,+0,0->0,+0
dp[i+1][tot]=min(dp[i+1][tot],dp[i][j]+j-tot+cnt-tot);
}
}
printf("%lld\n",dp[lim][0]);
return 0;
}