HDU-4611 Balls Rearrangement

题意:具体题意不大清楚,最后要处理一个这样的表达式:sum{ |i % a - i % b| },0 <= i < N 的取值很大,a、b均小于10^5。

分析:观察|i % a|和|i % b|可以发现其均为被模数的一个滚动剩余系,且中间的某些段的值是恒定的。再注意到其实处理到a和b的最小公倍数的时候又可以把最小公倍数循环的部分处理出来。我的做法就是维护好两个数,分别表示a和b两边谁出现最进出现 i % a 或者是 i % b 等于0的情况,因为此时两者的差值会发生改变,到达公倍数后则会出现两数同时到达该关键点,此时用总次数 N 除以公倍数加速。由于公倍数和枚举的间隔两者是此消彼长得关系,因此算法得以在较快时间内完成。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;

priority_queue<int>q1, q2;

int main() {
    int T;
    scanf("%d", &T);
    int a, b, n;
    while (T--) {
        scanf("%d %d %d", &n, &a, &b);
        while (!q1.empty()) q1.pop();
        while (!q2.empty()) q2.pop();
        q1.push(a), q2.push(b);
        int gap = 0, ti = -1;
        long long ret = 0;
        n -= 1;
        while (ti < n) {
            gap = min(q1.top(), q2.top());
            if (ti + gap > n) {
                gap = n - ti;
            }
            ti += gap;
            ret += 1LL*gap*abs(ti % a - ti % b);
            q1.pop(), q2.pop();
            int lst1 = q1.top()-gap;
            int lst2 = q2.top()-gap;
            if (!lst1 && !lst2) {
                ret += ((n+1)/(ti+1)-1) * ret;
                ti = (n+1)/(ti+1)*(ti+1)-1;
            }
            if (lst1 > 0)
                q1.push(lst1);
            else q1.push(a+lst1);
            if (lst2 > 0)
                q2.push(lst2);
            else q2.push(b+lst2);
        }
        printf("%I64d\n", ret);
    }
    return 0;
}
View Code

相关文章: