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; }