牛客练习赛 63 C
牛牛的揠苗助长
http://www.nowcoder.com/questionTerminal/70063f9318f9464e9340d8e0859085bc
题意
有 个数。
时刻开始时第
个数自增
,
时刻结束时可以选中一个数让其自增或者自减
,也可以什么都不做。求可能的最小时刻
,使得
时刻结束后所有数相等。
题解
可以转化为对每一个位置 求其为结束位置时可能的最小时刻
。则
。
到位置 之前会跑若干轮,设轮数最小为
。则
。
设数列为 。分析可知只需要关注数之间的相对大小关系,那么对于每一个位置
,实际上希望求解的是:找到一个
,使得
全部通过自增/自减变成
的次数最小。
容易发现最优的 就是
的中位数。找到了
就可以算出这个最小的操作次数
。然后考虑至少需要几轮才能做完这些操作,由于需要
可以算出最少需要对这
个数跑
轮。
这样就做完了。维护中位数可以用 multiset 来做。
#include <bits/stdc++.h>
#define INF 2000000000
using namespace std;
typedef long long ll;
int read(){
int f = 1, x = 0;
char c = getchar();
while(c < '0' || c > '9'){if(c == '-') f = -f; c = getchar();}
while(c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar();
return f * x;
}
int n, a[200005], b[200005];
ll sum1, sum2;
multiset<int> st1, st2;
void init(){
n = read();
for (int i = 1; i <= n; ++i)
a[i] = read(), b[i] = a[i];
sort(b + 1, b + n + 1);
}
void solve(){
int hf = ((n + 1) >> 1);
sum1 = sum2 = 0;
for (int i = 1; i <= hf; ++i)
st1.insert(b[i]), sum1 += b[i];
for (int i = hf + 1; i <= n; ++i)
st2.insert(b[i]), sum2 += b[i];
ll ans = LLONG_MAX;
for (int i = 1; i <= n; ++i){
int mid = *st1.rbegin();
if (a[i] <= mid){
st1.erase(st1.find(a[i]));
sum1 -= a[i];
}else {
st2.erase(st2.find(a[i]));
sum2 -= a[i];
st2.insert(mid);
sum2 += mid;
st1.erase(st1.find(mid));
sum1 -= mid;
}
if (st2.empty() || a[i] + 1 < *st2.begin()){
st1.insert(a[i] + 1);
sum1 += a[i] + 1;
}else {
st2.insert(a[i] + 1);
sum2 += a[i] + 1;
mid = *st2.begin();
st1.insert(mid);
sum1 += mid;
st2.erase(st2.find(mid));
sum2 -= mid;
}
mid = *st1.rbegin();
ll tt1 = 1ll * hf * mid - sum1 + sum2 - 1ll * (n - hf) * mid;
ll min_turn = max(0ll, (tt1 - i - 1) / n + 1);
ans = min(ans, min_turn * n + i);
}
printf("%lld\n", ans);
}
int main(){
init();
solve();
return 0;
} 