【LG5171Earthquake】
题面
题解
本题需要用到类欧几里得算法。
前置知识:类欧几里得
就是求函数$$varphi (a,b,c,n)=sum_{i=0}^n leftlfloorfrac {ai+b}c
ight
floor$$
的值(其实还有两种形式,但是我还不会这里不做介绍)。
它的几何意义是直线(y=frac {ax+b}c)在([0,n])下方或过直线的第一象限内的整点数
令(xi(i)=lfloorfrac {ai+b}c floor),
由结论(lfloorfrac{Ax}y floor = lfloorfrac{A(x;mod;y)}y floor + Alfloorfrac xy floor),
可以得到
然后可以得到(varphi(a,b,c,n)=varphi(amod c,bmod c, c, n)+frac {n(n+1)}2lfloorfrac ac floor+(n+1)lfloorfrac bc floor)。
现在我们将(xi(i))的值限制在了([0,n])之内,考虑将(varphi)用新的式子表示出来:
而右边艾弗森括号里的相当于统计有多少个数大于(frac {cd+c-b-1}{a}),就相当于(n-lfloorfrac {cd+c-b-1}{a} floor),那么
现在就可以递归处理了,至于复杂度,仔细思考一下发现和(gcd)复杂度一样,为(O(log n))。
代码实现:
long long f(long long a, long long b, long long c, long long n) {
if (!a) return b / c * (n + 1);
else if (a >= c || b >= c) return f(a % c, b % c, c, n) + n * (n + 1) / 2 * (a / c) + (n + 1) * (b / c);
else {
long long m = (a * n + b) / c;
return n * m - f(c, c - b - 1, a, m - 1);
}
}
关于此题
就是求(y=frac {c-ax}{b})下方或过直线在第一象限及正半轴上的整点数。
令(n=lfloorfrac ca floor),那么两端的点就为((0,frac cb),(n,frac {c-an}b))。
显然可以把两端的(y)值调换一下,那么两点变为((0,frac {c-an}b),(n,frac cb)),
此时这条直线变为(y=frac ab x+frac {c-an}{b}=frac {ax+(c;mod;a)}b),然后套到类欧的模板里再加上坐标轴上的贡献即可。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
long long a, b, c;
long long f(long long a, long long b, long long c, long long n) {
if (!a) return b / c * (n + 1);
else if (a >= c || b >= c) return f(a % c, b % c, c, n) + n * (n + 1) / 2 * (a / c) + (n + 1) * (b / c);
else {
long long m = (a * n + b) / c;
return n * m - f(c, c - b - 1, a, m - 1);
}
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
cin >> a >> b >> c;
printf("%lld
", f(a, c % a, b, c / a) + c / a + 1);
return 0;
}