欧几里得算法
\[\gcd(a,b)=\gcd(b,a\bmod b)
\]
显然至多递归 \(\log a\) 次。注意 \(\operatorname{lcm}(a, b)=\frac{ab}{\gcd(a,b)}\)
扩展欧几里得算法(exgcd)
对于方程
\[ax+by=c
\]
有解当且仅当\(\gcd(a,b)|c\)。这里假设 \(c=\gcd(a,b)\)。
因为有 \(\gcd(a,b)=\gcd(b,a\bmod b)\),又有
\[ \begin{aligned}
ax+by&=\gcd(a,b)\\
bx_1+(a\bmod b)y_1&=\gcd(b,a\bmod b)
\end{aligned}
\]
两式显然联立起来,对应系数,把取模运算拆开,有
\[ x=y_1,\ y=x_1-\left\lfloor\frac{a}{b}\right\rfloor y_1
\]
如此递归下去即可。边界条件为当 \(b\) 变为0时 \(x=1,y=0\)。
P5656 【模板】二元一次不定方程 (exgcd)
类欧几里得算法
给定 \(n,\,a,\,b,\,c\) ,分别求
$$
\sum_{i=0}^{n}\left\lfloor \frac{ai+b}{c} \right\rfloor,\ \sum_{i=0}^{n}{\left\lfloor \frac{ai+b}{c} \right\rfloor}^2,\ \sum_{i=0}^{n}i\left\lfloor \frac{ai+b}{c} \right\rfloor
$$
答案对 \(998244353\) 取模。多组数据。
式子看上去相当唬人。分开来做。设三个式子分别为 \(f(a,b,c,n),h(a,b,c,n),g(a,b,c,n)\),然后尝试去缩小数据规模。
推导 \(f\)
\(a\ge c\) 或者 \(b\ge c\) 时,将分子上的 \(a\) 和 \(b\) 拆开,有
\[ \begin{aligned}
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{aligned}
\]
当 \(a<c\) 且 \(b<c\) 时,这种方法就不能缩小规模了。考虑一种套路:和式展开后交换和式。
\[f(a,b,c,n)=\sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor
\]
把 \(\left\lfloor \frac{ai+b}{c} \right\rfloor\) 用求和式展开,有
\[ \sum_{i=0}^n\left\lfloor \frac{ai+b}{c} \right\rfloor
=\sum_{i=0}^n\sum_{j=0}^{\left\lfloor \frac{ai+b}{c} \right\rfloor-1}1
\]
交换和式
$$
{\large =\sum_{j=0}^{\left\lfloor \frac{an+b}{c} \right\rfloor-1}\sum_{i=0}^n\left[j<\left\lfloor \frac{ai+b}{c} \right\rfloor\right]}
$$
把 \(j<\left\lfloor \frac{ai+b}{c} \right\rfloor\) 这个条件转化一下,有
\[\begin{aligned}
j<\left\lfloor \frac{ai+b}{c} \right\rfloor
&\iff j+1\leq \left\lfloor \frac{ai+b}{c} \right\rfloor
\iff j+1\leq \frac{ai+b}{c}\\
&\iff jc+c\le ai+b \iff jc+c-b-1< ai\\
&\iff \left\lfloor\frac{jc+c-b-1}{a}\right\rfloor< i
\end{aligned}
\]
令 \(m=\left\lfloor \frac{an+b}{c} \right\rfloor\),那么原式化为
\[\begin{aligned}
f(a,b,c,n)&=\sum_{j=0}^{m-1}
\sum_{i=0}^n\left[i>\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{aligned}
\]
递归即可。此时 \(c\) 和 \(a\) 交换了位置,复杂度同欧几里得算法,为 \(O(\log n)\)。
推导 \(g\)
对于 \(g\),推导过程大致与 \(f\) 类似:
\(a\ge c\) 或 \(b\ge c\) 时
\[ 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}
\]
注意一个细节,有
\[\sum_{i=1}^ni^2=\frac{n(n+1)(2n+1)}{6}
\]
有了这个公式,上面这个式子就比较好推了。步骤几乎一模一样。
对于 \(a<c\) 且 \(b<c\) 的情况也类似,但稍有不同。
令 \(m=\left\lfloor\frac{an+b}{c}\right\rfloor\),有
\[ g(a,b,c,n)=\sum_{i=0}^ni\left\lfloor \frac{ai+b}{c} \right\rfloor=\sum_{j=0}^{m-1}
\sum_{i=0}^n\left[j<\left\lfloor\frac{ai+b}{c}\right\rfloor\right]\cdot i
\]
方便起见,设 \(t=\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\),
\[\begin{aligned}
&=\sum_{j=0}^{m-1}\sum_{i=0}^n[i>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{aligned}
\]
注意到倒数第二步的形式与 \(h\) 有点相似,此时同样是 \(c\) 和 \(a\) 交换了位置。因此我们就顺势写成 \(h\) 的形式,具体实现的时候直接调用。
推导 \(h\)
第一种情况类似,但有些复杂:
\(a\ge c\) 或 \(b\ge c\) 时
\[ \begin{aligned}
h(a,b,c,n)&=\sum_{i=0}^n \left\lfloor \dfrac{ai+b}{c}\right\rfloor^2=\sum_{i=0}^n\left(\left\lfloor\dfrac{a}{c}\right\rfloor i+\left\lfloor\frac{b}{c}\right\rfloor +\left\lfloor \dfrac{a\bmod c\times i+b\bmod c}{c}\right\rfloor\right)^2 \\
&=\sum_{i=0}^n\left(\left\lfloor\dfrac{a}{c}\right\rfloor^2 i^2+\left\lfloor\frac{b}{c}\right\rfloor^2 +\left\lfloor \dfrac{a \bmod c\times i+b\bmod c}{c}\right\rfloor^2+2\left\lfloor\dfrac{a}{c}\right\rfloor\left\lfloor\frac{b}{c}\right\rfloor i\right.\\
&\ \ \ \ \left.+2\left\lfloor\dfrac{a}{c}\right\rfloor\left\lfloor \dfrac{a\bmod c\times i+b\bmod c}{c}\right\rfloor i+2\left\lfloor\dfrac{b}{c}\right\rfloor\left\lfloor \dfrac{a\bmod c\times i+b\bmod c}{c}\right\rfloor\right)\\
&=\frac{n(n+1)(2n+1)}{6}\left\lfloor\dfrac{a}{c}\right\rfloor^2+(n+1)\left\lfloor\frac{b}{c}\right\rfloor^2+n(n+1)\left\lfloor\dfrac{a}{c}\right\rfloor\left\lfloor\frac{b}{c}\right\rfloor\\
&\ \ \ \ +2\left\lfloor\dfrac{a}{c}\right\rfloor g(a\bmod c,b\bmod c,c,n)+2\left\lfloor\dfrac{b}{c}\right\rfloor f(a\bmod c,b\bmod c,c,n)\\
&\ \ \ \ +h(a\bmod c,b\bmod c,c,n)
\end{aligned}
\]
第二种直接来:
\(a<c\) 且 \(b<c\) 时
令 \(m=\left\lfloor\frac{an+b}{c}\right\rfloor\),\(t=\left\lfloor\frac{cj+c-b-1}{a}\right\rfloor\)
\[h(a,b,c,n)=\sum_{i=0}^n \left\lfloor \dfrac{ai+b}{c}\right\rfloor^2
\]
将\(n^2\)写成\(\left(2\sum_{i=1}^n i\right)-n\)(算是一个不太常见的技巧?)
\[{\large \begin{aligned}
h(a,b,c,n)&=\sum_{i=0}^n\left(\left(2\sum_{j=1}^{\left\lfloor \frac{ai+b}{c}\right\rfloor}j\right)-\left\lfloor \dfrac{ai+b}{c}\right\rfloor\right)\\
&=\left(2\sum_{i=0}^n\sum_{j=0}^{\left\lfloor \frac{ai+b}{c}\right\rfloor-1}j+1\right)-f(a,b,c,n)\\
&=\left(2\sum_{j=0}^{m-1}\sum_{i=0}^n (j+1)\left[j< \left\lfloor \frac{ai+b}{c}\right\rfloor\right]\right)-f(a,b,c,n)
\end{aligned}}
\]
其中
\[\begin{aligned}
&\sum_{j=0}^{m-1}\sum_{i=0}^n (j+1)\left[j< \left\lfloor \frac{ai+b}{c}\right\rfloor\right]=\sum_{j=1}^{m-1}(j+1)\sum_{i=0}^n \left[j< \left\lfloor \frac{ai+b}{c}\right\rfloor\right]\\
=\,&\sum_{j=0}^{m-1}(j+1)\sum_{i=0}^n[i>t]=\sum_{j=0}^{m-1}(j+1)(n-t)\\
=\,&\dfrac{m(m+1)}{2}n-f(c,c-b-1,a,m-1) -g(c,c-b-1,a,m-1)
\end{aligned}
\]
所以
\[h(a,b,c,n)=m(m+1)n-2f(c,c-b-1,a,m-1)-2g(c,c-b-1,a,m-1)-f(a,b,c,n)
\]
三个函数都可以稳定缩小规模,理论复杂度 \(O(\log n)\),但是实际实现的时候可能会反复调用某一种函数的某一种取值。
而用 map 记忆化会让本就不小的常数捉襟见肘。因此用结构体去实现类似的功能,同时计算三种函数值。
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=998244353;/////////////////////////////////////////////////////mod mod mod mod mod mod mod mod mod mod mod mod mod mod mod mod
int ni2,ni3;
int ksm(int x,int k){int res=1ll;while(k){if(k&1) res=res*x%p;x=x*x%p,k>>=1;}return (res+p)%p;}
struct node {int f,g,h;};
void init(node &res){res.f=(res.f+p)%p,res.g=(res.g+p)%p,res.h=(res.h+p)%p;}
node work(int a,int b,int c,int n)
{
node res,tmp;
if(a==0){res.f=(n+1)*(((int)floor(1.0*b/c)+p)%p)%p,res.h=(n+1)*(((int)floor(1.0*b/c)+p)%p)%p*(((int)floor(1.0*b/c)+p)%p)%p,res.g=(n)*(n+1)%p*ni2%p*((int)floor(1.0*b/c)+p)%p;init(res);return res;}
if(a>=c||b>=c)
{
tmp=work(a%c,b%c,c,n);init(tmp);
res.f=(n*(n+1)%p*ni2%p*(((int)floor(1.0*a/c)+p)%p)%p+(n+1)*(((int)floor(1.0*b/c)+p)%p)%p+tmp.f%p+p)%p;
res.g=(tmp.g+(((int)floor(1.0*a/c)+p)%p)*n%p*(n+1)%p*(2*n+1)%p*ni2%p*ni3%p+(((int)floor(1.0*b/c)+p)%p)*n%p*(n+1)%p*ni2%p+p)%p;
res.h=((n*(n+1)%p*(2*n+1)%p)*ni2%p*ni3%p*(((int)floor(1.0*a/c)+p)%p)%p*(((int)floor(1.0*a/c)+p)%p)%p+
(n+1)*(((int)floor(1.0*b/c)+p)%p)%p*(((int)floor(1.0*b/c)+p)%p)%p+
n*(n+1)%p*(((int)floor(1.0*a/c)+p)%p)%p*(((int)floor(1.0*b/c)+p)%p)%p+
2*(((int)floor(1.0*a/c)+p)%p)%p*tmp.g%p+2*(((int)floor(1.0*b/c)+p)%p)%p*tmp.f+tmp.h+p)%p;
init(res);return res;
}
else
{
int m=(a*n+b)/c;
tmp=work(c,c-b-1,a,m-1);
m=(m+p)%p;init(tmp);
res.f=(n*m%p-tmp.f+p)%p;
res.g=(m*n%p*(n+1)%p-tmp.h-tmp.f+p)%p*ni2%p;
res.h=(m*(m+1)%p*n%p-2*tmp.f%p-2*tmp.g%p-res.f+p)%p;
init(res);return res;
}
}
void solve()
{
int n,a,b,c;cin>>n>>a>>b>>c;
node res=work(a,b,c,n);cout<<(res.f+p)%p<<' '<<(res.h+p)%p<<' '<<(res.g+p)%p<<'\n';
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
ni2=ksm(2,p-2),ni3=ksm(3,p-2);int T;cin>>T;while(T--) solve();
}