【算法学习笔记】类欧几里得算法
一个基础的数论问题。
试求
的值,其中: ,
在Atcoder的AC库中有这样一个函数可以在
函数代码 ↓
using ll = long long;
ll floor_sum(ll n, ll m, ll a, ll b) {
ll ans = 0;
if (a >= m) {
ans += (n - 1) * n * (a / m) / 2;
a %= m;
}
if (b >= m) {
ans += n * (b / m);
b %= m;
}
ll y_max = (a * n + b) / m, x_max = (y_max * m - b);
if (y_max == 0) return ans;
ans += (n - (x_max + a - 1) / a) * y_max;
ans += floor_sum(y_max, a, m, (a - x_max % a) % a);
return ans;
}
好奇它的证明过程,然后在 OI wiki
上找到相应文档,这个算法名为:类欧几里德算法
个人证明
另外补上个人证明:
当
或 时, 当
并且 时, 设
, 然后递归至
即可.
例题
数形结合, 把式子稍微简单转换一下, 套用类欧几里得算法即可.
引入
设
其中
这个式子和我们以前见过的式子都长得不太一样。带向下取整的式子容易让人想到数论分块,然而数论分块似乎不适用于这个求和。但是我们是可以做一些预处理的。
如果说
那么问题转化为了
要加快一个和式的计算过程,所有的方法都可以归约为 贡献合并计算。但你发现这个式子的贡献难以合并,怎么办?将贡献与条件做转化 得到另一个形式的和式。具体地,我们直接把原式的贡献变成条件:
现在多了一个变量
这样做的目的是让
然后可以做一些变换
最后一步,向下取整得到:
这一步的重要意义在于,我们可以把变量
这是一个递归的式子。并且你发现
容易发现时间复杂度为
同时关于 类欧几里德算法
有两个函数的拓展
扩展
理解了最基础的类欧几里德算法,我们再来思考以下两个变种求和式:
推导 g
我们先考虑
接下来考虑
这时我们设
推导 h
同样的,首先取模:
考虑
我们发现这个平方不太好处理,于是可以这样把它拆成两部分:
这样做的意义在于,添加变量
接下来考虑化简前一部分:
因此
在代码实现的时侯,因为
模板代码实现
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)
2020-03-26 PTA | 06-图3 六度空间 (30分)
2020-03-26 PTA | 06-图1 列出连通集 (25分)