题目链接:
http://codeforces.com/contest/672/problem/D
题意:
给你一个数组,每次操作,最大数减一,最小数加一,如果最大数减一之后比最小数加一之后要小,则取消操作,现在给你操作的次数,问操作之后最大数减最小数的最小值。
题解:
问题要求得是min(k次操作之后的最大数-k次操作之后的最小数),而这两个数可以独立求出来,我们先用二分求k次操作之后的最小数的最大取值,然后,再用二分求k次操作之后的最大数的最小可能取值。
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn = 500000 + 10; const int INF = 0x3f3f3f3f; typedef __int64 LL; LL arr[maxn]; int n, k,Ma,Mi; bool ok1(int x) { LL cnt = 0; for (int i = 0; i < n; i++) { cnt += max((LL)0, x - arr[i]); } if (cnt <= k) return true; return false; } int bs1() { int l = Mi,r = Ma+1; while (l + 1 < r) { int mid = l + (r - l) / 2; if (ok1(mid)) l = mid; else r = mid; } return l; } bool ok2(int x) { LL cnt = 0; for (int i = 0; i < n; i++) { cnt += max((LL)0, arr[i]-x); } if (cnt <= k) return true; return false; } int bs2() { int l = Mi-1, r = Ma; while (l + 1 < r) { int mid = l + (r - l) / 2; if (ok2(mid)) r = mid; else l = mid; } return r; } void init() { Mi = INF; Ma = -1; } int main() { while (scanf("%d%d", &n, &k) == 2 && n) { init(); LL sum = 0; int mi, ma; for (int i = 0; i < n; i++) { scanf("%I64d", arr + i); sum += arr[i]; Ma = max((LL)Ma, arr[i]), Mi = min((LL)Mi, arr[i]); } mi = bs1(); ma = bs2(); int ans = 0; if (mi >= ma) { if (sum%n) ans = 1; } else { ans = ma - mi; } printf("%d\n", ans); } return 0; }