题意
N 个盒子,原本有一些球;每次可以向 M 个不同的盒子里面各放一个球,问最少要多少次操作能让盒子里面的球数量相等。不可以输出 -1.
解法:
一开始写了一个姿势 wa了,换了一个姿势就过了
方法是——记录所有盒子的最大值 Max , 和 Sum , 最小值 Min。
那么如果最后能够达成,高度为H , 那么肯定满足以下三个条件
1、H >= Max
2、(n * H - Sum) % m == 0
3、要做的操作数T = (n * H - Sum) / m , 那么 T >= H - Min【重要!!!】
那么要做的事情就是首先解第二个方程,可以将他转化成最简单的 ax = b (mod n) 形式——
(n * H) = (m - Sum % m) % m (mod m)
解这个方程,首先假设 A * x + B * m = C ,用 extendGCD 求出来一组解和C 。 那么如果 (m - Sum % m) % C != 0 (或者是 Sum mod GCD(n , m) != 0) 的话一定没解。。
其次,再讲问题转化一下,我们求出来最小的 Hmin , 那么 H 的解就可以转化成 H + i * m / C ....巧合的是,我们之前用 extendGCD 已经求出来了一个 n 但是 n 可能是负数。
那么就每次加上 m / C 一直到 n >=
Max 为止。
但是这就是答案了么?明显不是,看下面这组数据
3
0 100000 100000
明显答案是 200000 , 不是 50000 。 那怎么办呢?就要用到第 3 个性质
方法是 二分。假设答案是 Hmin + ans * m / C . 那么枚举上下界二分即可,要求满足的是 T >= H + i * m / C
代码
LL n , m;
LL a[20000];
void solve(){
RD(n , m);
LL s = 0 , h = 0 , l = 1e9;
REP(i , n) {
RD(a[i]);
checkMax(h , a[i]);
checkMin(l , a[i]);
s += a[i];
}
LL d = GCD(n , m);
if (s % d != 0){
puts("-1");
return;
}
LL x , y;
d = extendGCD(n , m , x , y);
x *= s / d;
LL inc = m / d;
LL ins = (h - x) / inc;
x += ins * inc;
if (x < h) x += inc;
// cout << inc << endl;
LL low = 0 , high = 1e9 , mid , ans = 1e9;
do{
mid = low + high >> 1;
LL xx = x + inc * mid;
LL t = (xx * n - s) / m;
if (xx - l <= t){
checkMin(ans , mid);
high = mid - 1;
}
else low = mid + 1;
}while(low <= high);
printf("%lld\n" , ((x + inc * ans) * n - s) / m);
}
int main(){
// freopen("0.txt" , "r" , stdin);
Rush solve();
}