WIKIOI
wiki(I:)
比赛相关
工具软件
语言基础
算法基础
搜索
动态规划
字符串
数学
数据结构
图论
计算几何
杂项
专题
数学部分简介
符号
复数
位运算
快速幂
进位制
高精度计算
平衡三进制
素数
最大公约数
欧拉函数
筛法
欧拉定理 & 费马小定理
类欧几里德算法
裴蜀定理
乘法逆元
线性同余方程
中国剩余定理
二次剩余
BSGS
原根
卢卡斯定理
莫比乌斯反演
杜教筛
Min_25 筛
分解质因数
多项式部分简介
拉格朗日插值
快速傅里叶变换
快速数论变换
快速沃尔什变换
多项式求逆
多项式开方
多项式除法|取模
多项式对数函数|指数函数
多项式牛顿迭代
多项式多点求值|快速插值
多项式三角函数
多项式反三角函数
常系数齐次线性递推
生成函数简介
普通生成函数
指数生成函数
向量
矩阵
高斯消元
线性基
线性规划简介
单纯形算法
排列组合
卡特兰数
斯特林数
贝尔数
伯努利数
康托展开
容斥原理
抽屉原理
概率初步
置换群
斐波那契数列
博弈论
牛顿迭代法
数值积分
分段打表
64 objects
本站非官方,所收集资源均来源于网络。
类欧几里德算法 - 数学
author: sshwy, FFjet 类欧几里德算法由洪华敦在 2016 年冬令营营员交流中提出的内容,其本质可以理解为,使用一个类似辗转相除法来做函数求和的过程。 ## 引入 设 $$ f(a,b,c,n)=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor $$ 其中 $a,b,c,n$ 是常数。需要一个 $O(\log n)$ 的算法。 这个式子和我们以前见过的式子都长得不太一样。带向下取整的式子容易让人想到数论分块,然而数论分块似乎不适用于这个求和。但是我们是可以做一些预处理的。 如果说 $a\ge c$ 或者 $b\ge c$ ,意味着可以将 $a,b$ 对 $c$ 取模以简化问题: $$ \begin{split} f(a,b,c,n)&=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor\\\\ &=\sum_{i=0}^n\left\lfloor \frac{\left(\left\lfloor\frac{a}{c}\right\rfloor c+a\bmod c\right)i+\left(\left\lfloor\frac{b}{c}\right\rfloor c+b\bmod c\right)}{c}\right\rfloor\\\\ &=\frac{n(n+1)}{2}\left\lfloor\frac{a}{c}\right\rfloor+(n+1)\left\lfloor\frac{b}{c}\right\rfloor+ \sum_{i=0}^n\left\lfloor\frac{\left(a\bmod c\right)i+\left(b\bmod c\right)}{c} \right\rfloor\\\\ &=\frac{n(n+1)}{2}\left\lfloor\frac{a}{c}\right\rfloor +(n+1)\left\lfloor\frac{b}{c}\right\rfloor+f(a\bmod c,b\bmod c,c,n) \end{split} $$ 那么问题转化为了 $a
\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor \right]\\\\ &=\sum_{j=0}^{m-1} n-\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\\\\ &=nm-f\left(c,c-b-1,a,m-1\right) \end{split} $$ 这是一个递归的式子。并且你发现 $a,c$ 分子分母换了位置,又可以重复上述过程。先取模,再递归。这就是一个辗转相除的过程,这也是类欧几里德算法的得名。 容易发现时间复杂度为 $O(\log n)$ 。 ## 扩展 理解了最基础的类欧几里德算法,我们再来思考以下两个变种求和式: $$ g(a,b,c,n)=\sum_{i=0}^ni\left\lfloor \frac{ai+b}{c} \right\rfloor\\\\ h(a,b,c,n)=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor^2 $$ ### 推导 g 我们先考虑 $g$ ,类似地,首先取模: $$ g(a,b,c,n) =g(a\bmod c,b\bmod c,c,n)+\left\lfloor\frac{a}{c}\right\rfloor\frac{n(n+1)(2n+1)}{6}+\left\lfloor\frac{b}{c}\right\rfloor\frac{n(n+1)}{2} $$ 接下来考虑 $a
t]\cdot i\\\\ &=\sum_{j=0}^{m-1}\frac{1}{2}(t+n+1)(n-t)\\\\ &=\frac{1}{2}\left[mn(n+1)-\sum_{j=0}^{m-1}t^2-\sum_{j=0}^{m-1}t\right]\\\\ &=\frac{1}{2}[mn(n+1)-h(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1)] \end{split} $$ ### 推导 h 同样的,首先取模: $$ \begin{split} h(a,b,c,n)&=h(a\bmod c,b\bmod c,c,n)\\\\ &+2\left\lfloor\frac{b}{c}\right\rfloor f(a\bmod c,b\bmod c,c,n) +2\left\lfloor\frac{a}{c}\right\rfloor g(a\bmod c,b\bmod c,c,n)\\\\ &+\left\lfloor\frac{a}{c}\right\rfloor^2\frac{n(n+1)(2n+1)}{6}+\left\lfloor\frac{b}{c}\right\rfloor^2(n+1) +\left\lfloor\frac{a}{c}\right\rfloor\left\lfloor\frac{b}{c}\right\rfloor n(n+1) \end{split} $$ 考虑 $a
t]\\\\ =&\sum_{j=0}^{m-1}(j+1)(n-t)\\\\ =&\frac{1}{2}nm(m+1)-\sum_{j=0}^{m-1}(j+1)\left\lfloor \frac{jc+c-b-1}{a} \right\rfloor\\\\ =&\frac{1}{2}nm(m+1)-g(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1) \end{split} $$ 因此 $$ h(a,b,c,n)=nm(m+1)-2g(c,c-b-1,a,m-1)-2f(c,c-b-1,a,m-1)-f(a,b,c,n) $$ 在代码实现的时侯,因为 $3$ 个函数各有交错递归,因此可以考虑三个一起整体递归,同步计算,否则有很多项会被多次计算。这样实现的复杂度是 $O(\log n)$ 的。 ### "[模板题代码实现](https://www.luogu.com.cn/problem/P5170)" ```cpp #include
#define int long long using namespace std; const int P = 998244353; int i2 = 499122177, i6 = 166374059; struct data { data() { f = g = h = 0; } int f, g, h; }; // 三个函数打包 data calc(int n, int a, int b, int c) { int ac = a / c, bc = b / c, m = (a * n + b) / c, n1 = n + 1, n21 = n * 2 + 1; data d; if (a == 0) { // 迭代到最底层 d.f = bc * n1 % P; d.g = bc * n % P * n1 % P * i2 % P; d.h = bc * bc % P * n1 % P; return d; } if (a >= c || b >= c) { // 取模 d.f = n * n1 % P * i2 % P * ac % P + bc * n1 % P; d.g = ac * n % P * n1 % P * n21 % P * i6 % P + bc * n % P * n1 % P * i2 % P; d.h = ac * ac % P * n % P * n1 % P * n21 % P * i6 % P + bc * bc % P * n1 % P + ac * bc % P * n % P * n1 % P; d.f %= P, d.g %= P, d.h %= P; data e = calc(n, a % c, b % c, c); // 迭代 d.h += e.h + 2 * bc % P * e.f % P + 2 * ac % P * e.g % P; d.g += e.g, d.f += e.f; d.f %= P, d.g %= P, d.h %= P; return d; } data e = calc(m - 1, c, c - b - 1, a); d.f = n * m % P - e.f, d.f = (d.f % P + P) % P; d.g = m * n % P * n1 % P - e.h - e.f, d.g = (d.g * i2 % P + P) % P; d.h = n * m % P * (m + 1) % P - 2 * e.g - 2 * e.f - d.f; d.h = (d.h % P + P) % P; return d; } int T, n, a, b, c; signed main() { scanf("%lld", &T); while (T--) { scanf("%lld%lld%lld%lld", &n, &a, &b, &c); data ans = calc(n, a, b, c); printf("%lld %lld %lld\n", ans.f, ans.h, ans.g); } return 0; } ```