[数论]中南大学 2013 校赛 G 题解题报告

来源:互联网 发布:mac双击打不开文件 编辑:程序博客网 时间:2024/04/29 07:34
Balls and Boxes

题意


微笑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();}


原创粉丝点击