万能欧几里得算法

问题

有一条直线 y=Px+KQ,其中 P00K<Q。有两种操作 U,R,从左到右扫描这条直线在 (0,L] 中的部分,若直线跨越了 y=k(kZ) 这条直线,则执行 U 操作;若直线跨越了 x=k(kZ) 这条直线,则执行 R 操作。若同时跨越则先执行 U 再执行 R

操作满足结合律,且合并两个操作的时间复杂度是 O(1)

万能欧几里得算法

我们设 solve(P,Q,K,L,U,R) 为上述问题的答案。类似于欧几里得算法,我们讨论 PQ 的大小关系:

  • PQ,那么每个 R 操作前一定有至少 PQU 操作。可以把 R 操作和这些 U 操作合并起来,递归到 solve(PmodQ,Q,K,L,U,RUPQ)

  • P<Q,那么需要尝试递归到 solve(Q,P,) 之类的东西。原先的问题是,第 xR 前面有 Px+KQU,那么可以算出第 yU 前有 QyK1PR

    但直接递归下去不满足 0K<Q。考虑把第一个 U 及以前的部分截去,这样 K 就会变成 QK1。而第一个 U 前有 QK1PR,于是递归到 RQK1PUsolve(Q,P,QK1,C,R,U),其中 C=PL+KQU 的个数。

    在最后一个 U 后面仍有一些 R,可以算出这样的 R 的个数是 LQCK1P,再乘上一些 R 即可。

尽管这个过程中需要做快速幂,但由于快速幂的指数可以估算为 QP,而 log(QP)=logQlogP,递归到下一层后 P,Q 交换,所以这个 log 可以被抵消掉。于是时间复杂度为 O(logmax(P,Q))

代码

ll Div(ll a, ll b, ll c, ll d) {
	return (__int128(a) * b + c) / d;
}
template<typename Info>
Info _Euclid(ll p, ll q, ll k, ll xlim, const Info& u, const Info& r) {
	if (!xlim) return Info();
	if (p >= q) return _Euclid(p % q, q, k, xlim, u, u * (p / q) + r);
	ll m = Div(xlim, p, k, q);
	if (!m) return r * xlim;
	ll cnt = xlim - Div(q, m, -k - 1, p);
	return r * ((q - k - 1) / p) + u + _Euclid(q, p, (q - k - 1) % p, m - 1, r, u) + r * cnt;
}
template<typename Info>
Info Euclid(ll p, ll q, ll k, ll xlim, const Info& u, const Info& r) {
	assert(0 <= p && 0 < q && 0 <= k && 0 <= xlim);
	return u * (k / q) + _Euclid(p, q, k % q, xlim, u, r);
}

作者:alan-zhao-2007

出处:https://www.cnblogs.com/alan-zhao-2007/p/euclid-algorithm.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Alan_Zhao_2007  阅读(65)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
历史上的今天:
2021-06-18 P3521 [POI2011]ROT-Tree Rotations - 线段树合并
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题