题意
有一个长度为
n
n
n的数组
a
a
a,每次操作可以给一个数加上
2
k
2^k
2k,其中
k
k
k可以是任意非负整数。问最少需要多少次操作来使得该数组的每个元素都相等。
n
≤
1
0
5
,
a
i
≤
1
0
17
n\le10^5,a_i\le10^{17}
n≤105,ai≤1017
分析
假设最终每个数都会变为
x
x
x,显然
x
≥
m
a
x
(
a
i
)
x\ge max(a_i)
x≥max(ai)那么需要的操作次数就是
∑
i
=
1
n
b
i
t
c
o
u
n
t
(
x
−
a
i
)
\sum_{i=1}^nbitcount(x-a_i)
i=1∑nbitcount(x−ai)
设
b
i
=
a
n
−
a
i
,
t
=
x
−
a
n
b_i=a_n-a_i,t=x-a_n
bi=an−ai,t=x−an,现在要最小化
∑
i
=
1
n
b
i
t
c
o
u
n
t
(
t
+
b
i
)
\sum_{i=1}^nbitcount(t+b_i)
i=1∑nbitcount(t+bi)
考虑dp,当计算到某一位时,如果要计算答案对于每一个
i
i
i我们需要知道
t
+
b
i
t+b_i
t+bi的前一位是否有进位,
b
i
b_i
bi的这一位和
t
t
t的这一位。其中
b
i
b_i
bi的这一位已经知道,
t
t
t的这一位可以在dp时枚举,如果我们暴力记录前一位是否进位,状态数为
O
(
2
n
)
O(2^n)
O(2n),显然无法接受。
但可以发现
t
+
b
i
t+b_i
t+bi的前
k
−
1
k-1
k−1位发生了进位当且仅当
t
 
m
o
d
 
2
k
+
b
i
 
m
o
d
 
2
k
≥
2
k
t\bmod 2^k+b_i\bmod 2^k\ge2^k
tmod2k+bimod2k≥2k
也就是说如果我们把
b
i
 
m
o
d
 
2
k
b_i\bmod 2^k
bimod2k按照从小到大排序,那么会发生进位的就是一个后缀,这样状态数就可以做到
O
(
n
)
O(n)
O(n)了。
还有一个结论就是答案不会超过
m
a
x
(
b
i
)
max(b_i)
max(bi),具体证明可以看官方题解。
对每一位转移时的排序可以做到
O
(
n
)
O(n)
O(n)。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=100005;
const LL inf=(LL)2e18;
int n,now,s[N];
LL a[N],f[65][N],bin[65],b[N];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
std::sort(a+1,a+n+1);
for (int i=1;i<=n;i++) a[i]=a[n]-a[i];
for (int i=0;i<=60;i++)
for (int j=0;j<=n;j++)
f[i][j]=inf;
f[0][0]=0;
bin[0]=1;
for (int i=1;i<=60;i++) bin[i]=bin[i-1]*2;
for (int i=1;i<=60;i++)
{
for (int j=1;j<=n;j++) s[j]=s[j-1]+((a[j]&bin[i-1])?1:0);
for (int j=0;j<=n;j++)
{
if (f[i-1][j]==inf) continue;
int was=j-s[j]+s[n]-s[j],cnt=s[j];
f[i][cnt]=std::min(f[i][cnt],f[i-1][j]+was);
was=s[j]+(n-j)-(s[n]-s[j]);cnt=j+s[n]-s[j];
f[i][cnt]=std::min(f[i][cnt],f[i-1][j]+was);
}
int k=0;
for (int j=1;j<=n;j++) if (a[j]&bin[i-1]) b[++k]=a[j];
for (int j=1;j<=n;j++) if (!(a[j]&bin[i-1])) b[++k]=a[j];
for (int j=1;j<=n;j++) a[j]=b[j];
}
printf("%lld\n",f[60][0]);
return 0;
}