辗转相除法简介qwq

其实是要讲万欧啦ovo,不过似乎本质就是类欧。但是似乎比类欧更容易学,扩展性更强,完爆类欧。参考链接是这篇,不过笔误较多,所以我稍加整理,希望大佬们轻喷/dk

首先是我们要求的东西:

给定非负整数 \(p,q,r,n\),且 \(q>0\)。考虑一个由 UR 组成的序列:由 \(n\) 段拼接成,第 \(i(1\leq i\leq n)\) 段为 \(U^{b_i-b_{i-1}}R\),其中 \(b_i(0\leq i\leq n)=\lfloor\frac{pi+r}{q}\rfloor\)。例如 \(p=5,q=3,r=4,n=8\),对应的序列是 UURURUURUURURUURUURUR

里面的 \(U\)\(R\) 不一定是一个数,可以理解为两种"操作"。比如在类欧的经典形式中,假如把目前的 \(i\) 记作 \(x\)\(\lfloor\frac{ai+b}{c}\rfloor\) 记作 \(y\),则 U 对应 y+=1R 对应 x+=1 且将此时的 \(y\) 加入答案。

你的目标时快速求出这个序列对应的操作复合的结果,也就是它们的乘积。实际上,我们只需要把这两种操作看成元素,乘法设为复合,它们能构成半群即可。(也即,我们只需要要求结合律,在下述说明中,操作流程只用到了幂和乘法)

接下来,我们正式开始详解这个四行代码的算法!

情况 \(1\):若 \(r\geq q\),那么显然,将 \(r\leftarrow r\bmod q\) 不会有影响。

情况 \(2\):若 \(p\geq q\),那么每一段末尾都会是 \(U^{\lfloor\frac{p}{q}\rfloor}R\) 的形式,因此可以令 \(p\leftarrow p\bmod q,R\leftarrow U^{\lfloor\frac{p}{q}\rfloor}R\)。(这部分也是显然的。如果你在这里有问题,可以理解成下一段比上一段多了 \(p\) 个,等价于多了 \(p\bmod q\) 个,且"白送"了 \(\lfloor\frac{p}{q}\rfloor\) 个大小为 \(q\) 的整段)

剩下的情况就是 \(p<q\)。一个显然的性质是不会存在相邻的 U,也就是说从"感觉上",变成了类似 RRURURRURRU 的形式,这提示我们去交换 UR

U'=R,R'=U。考虑每个 R' 之前 U' 的数量。在原序列上就是对于一个 \(y\),求满足 \(y\gt \lfloor\frac{px+r}{q}\rfloor\)\(x\in \Z^+\) 数量。这可以通过一个关于取整符号的简单变换来求出。

把下取整改上取整,等价于 \(y\geq \lceil\frac{px+r+1}{q}\rceil\),即 \(qy\geq px+r+1\)。这意味着 \(x\leq \lfloor\frac{qy-r-1}{p}\rfloor\),就变成了一个几乎和原来形式完全一样的新问题!设 \(m\) 为原序列总共 U 的数量(为 \(\lfloor\frac{pn+r}{q}\rfloor\)),则问题从 \((p,q,r,n)\) 转为了 \((q,p,-r-1,m)\)

不过容易发现会产生两个问题,一个是 \(r'\) 项为负数,另一个是右对齐(即序列必须要求以 R' 结尾)。

问题二的解决方式很容易想到,就是在原序列上把一段后缀 R 直接乘掉。这个个数可以通过总个数减去最后一个 U 之前的个数得到,即 \(n-\lfloor\frac{qm-r-1}{p}\rfloor\),在递归后的结果的基础上右乘一个快速幂就好。

问题一的解决方式也不难,因为 \(q-r-1\geq 0\) 是合法的(这个值是 \(qy-r-1\)\(y=1\) 处的取值),所以只需要对后 \(m-1\) 个段做递归,第一个段暴力处理就好了。也就是左乘上 \(R^{\lfloor\frac{q-r-1}{p}\rfloor}U\)

最后边界从这里也很容易看得出来,前两种情况不会改变 \(n\) 的值,最后一种情况在 \(m\geq 1\) 时都可以继续递归下去,只需要 \(m=0\) 的时候返回 \(R^n\) 即可。于是一个 \(\operatorname{polylog}\) 复杂度的万欧算法就出来了。

仔细想想或者观察下代码,操作二中快速幂的量级为 \(O(p/q)\),操作三中为 \(O(q/p)\)。不妨快速幂的复杂度就是 \(\log\)。假如把它们捆绑在一起,那么只需要考虑操作二,且每轮一个数在付了 \(\log t\) 的代价的同时也会比掉 \(t\),因此复杂度是 \(O(\log p+\log q)\) 的。

因此总复杂度为 \(O((\log pq)\cdot Tpow)\),其中 \(Tpow\) 为信息乘法部分的复杂度。

放个代码(类欧板子,仅展示万欧部分的代码,信息合并是 trivial 的):

apple solve(int p,int q,int r,long long n,apple U,apple R){
	r%=q;
	if(p>=q)return solve(p%q,q,r,n,U,(U^(p/q))*R);
	long long m=(p*n+r)/q;
	if(!m)return R^n;
	long long zh=n-(q*m-r-1)/p;
	return (R^((q-r-1)/p))*U*solve(q,p,q-r-1,m-1,R,U)*(R^zh);
}

然后我来列举一些万欧的简单应用:

luogu P5170:设 \(x=i,y=\frac{ai+b}{c}\),忽略掉 \(i=0\) 的答案,那么答案相当于所有 \(x\) 变化的位置的 \(y,y^2,xy\) 之和。采用类似线段树维护区间信息的方式,维护所有这些时刻(即 R 结尾的时刻)的 \(x,y,\sum x,\sum y,\sum y^2,\sum xy\) 即可。当然,也可以把 UR 都看作矩阵,不过这样常数会更大。

loj 6440:这个信息看起来不太能用矩阵维护,但是它有可合并的性质,所以类似地维护 \(A^x,B^y,\sum A^xB^y\) 即可。

min of mod of linear:也是同样地维护信息。此时并不是加和,是最值问题,但把 \((ax+b)\bmod m\) 当作 \(ax+b-m\frac{ax+b}{m}=ax+b-my\),同样维护信息即可。

练习题 Codeforces 868G:一种可能的做法(我胡的,不确定正确性)是求无穷和,复杂度是不变的,比较好想,但可能要列一些公式;此外还可以解方程,就是常见的万欧形式了。

变体:

luogu P5172:不妨 \(\sqrt r\) 为无理数。那么此时不会存在非平凡整点 \((x,y)\)。设 \(k\) 为那个无理数,则每次只需要么 \(k\leftarrow k-\lfloor k\rfloor\),要么 \(k\leftarrow \frac{1}{k}\) 操作下去即可。复杂度证明方式和之前不太一样,一种考虑方法是在 \(k<0.5\)\(n\) 会至少减半,\(0.5<k<1\) 时第一次操作成 \((\frac{1}{k},nk)\),第二次操作成 \((\frac{k}{1-k},n(1-k))\),所以 \(n\) 也会减半,复杂度就是 \(O(\log n)\) 了。(不过这题可能会卡精度,建议使用 __float128,并且开根直接用牛顿迭代)。

posted @ 2024-07-03 19:26  maihe  阅读(167)  评论(0)    收藏  举报