我们设当前所有房子中最低的高度是x,那么允许出现的最高高度就是x+m了,于是将所有比x低的高度都修改为x,比x+m高的修改为x+m,处于[x,x+m]的则无需修改.
展开这个式子,发现是一个开口向上的二次函数,其中X为自变量,其余都是已知,满足先减后增的性质,故可用三分求极值.
这里我们先将B数列排序,再预处理他的前缀和与前缀平方和,那么O(log*log)足够了。(第二个log为每次找比X小的数,比X+M大的数)
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 2e5 + 1;
typedef long long ll;
ll n, m, a[maxn];
ll sum[maxn]; //前缀和
ll mul[maxn]; //前缀平方和
int BinarySearchLeft(int h) //返回h的左边第一个元素下标
{
int l = 1, r = n, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if(h <= a[mid]) r = mid - 1;
else l = mid + 1;
}
return r;
}
int BinarySearchRight(int h) //返回h的有边第一个元素下标
{
int l = 1, r = n, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if(h >= a[mid]) l = mid + 1;
else r = mid - 1;
}
return l;
}
ll f(int h) //函数f(x)
{
int l = BinarySearchLeft(h);
int r = BinarySearchRight(h + m);
ll tmp = mul[l] + 1LL * h * h * l - 2LL * h * sum[l];
if(r <= n)
tmp += mul[n] - mul[r - 1] + 1LL * (h + m) * (h + m) * (n - r + 1) - 2LL * (h + m) * (sum[n] - sum[r - 1]);
return tmp;
}
ll SanFen(int l, int r) //三分法算最优解
{
while (l < r - 1)
{
int mid = (l + r) >> 1;
int midr = (mid + r) >> 1;
if(f(mid) > f(midr))
l = mid;
else
r = midr;
}
return f(l) < f(r) ? f(l) : f(r);
}
int main()
{
ll ave = 0, ans = 0, minans = 1e18;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i], ave += a[i];
sort(a + 1, a + n + 1);
ave /= n;
for (int i = 1; i <= n; i++) //预处理
{
sum[i] = sum[i - 1] + a[i];
mul[i] = mul[i - 1] + a[i] * a[i];
}
cout << SanFen(ave - m, ave + m) << endl;
return 0;
}