类欧几里得学习笔记

省选前学不会用到的新知识点多有趣(

1. 普通的类欧几里得

[sum_{x=1}^nlfloorfrac{px+r}{q} floor ]

对于 (p<0)(pge q)(r<0)(rge q) 的情况,可以直接把整除的部分提出来,设 (p'=pmod q,r'=rmod q)

[sum_{x=1}^nlfloorfrac{p'x+r'}{q} floor+lfloorfrac{p}{q} floorcdot frac{n(n+1)}{2}+lfloorfrac{r}{q} floorcdot n ]

对于其他情况,我们可以改计算 (y=frac{px+r}{q}) 的下面的整点个数变为上面的整点个数。设 (m=lfloorfrac{pn+r}{q} floor)

[egin{aligned} Ans&=sum_{d=0}^{m-1}sum_{i=0}^n[lfloorfrac{pi+r}{q} floor>d] \ &=sum_{d=0}^{m-1}sum_{i=0}^n[i>frac{qd+q-r-1}{p}] \ &=nm-sum_{x=0}^{m-1}lfloorfrac{qx+q-r-1}{p} floor end{aligned} ]

于是就递归到了更小的情况,时间复杂度 (O(logmax{p,q}))

然后你暴推式子,终于过了 luogu 模板题的时候,你翻到了 LOJ 的模板题

(你兴奋地骂了句cnm就把这题跳了)

2. 万能欧几里得

实际上有一个有技巧的推柿子方法。

就是对于一条直线 (y=frac{px+r}{q}),当它经过一个横坐标为整数的点时记一个字符 ( ext{R}),经过一个纵坐标为整数的点时记一个字符 ( ext{U}),特别的,经过整点的时候先记 ( ext{U}) 再记 ( ext{R}),得到了一个字符串,每个字符表示可能被计算贡献的一部分。我们用一个结构体Node表示一个字符串的贡献。以上面的第一道题为例,设 (cnt1,cnt2)( ext{U})( ext{R}) 的个数,(sum) 表示当前的答案。

我们定义两个字符串的拼接为两个Node的乘法。这个乘法必须满足结合律,但不需要满足交换律(实际上也不太可能)。

[egin{aligned} &(cnt1,cnt2,sum) imes(cnt1',cnt2',sum') \ =&(cnt1+cnt1',cnt2+cnt2',sum+sum'+cnt1cdot cnt2’) end{aligned} ]

(不太理解的话可以画画图)

于是 ( ext{U}=(1,0,0), ext{R}=(0,1,0))。我们用函数 ( exttt{calc(p,q,r,n,U,R)}) 计算 (y=frac{px+r}{q}(xin (0,n])) 形成的字符串,往上/右的贡献分别为 ( ext{U,R}) 时的答案。规定 (0le r<q)

(pge q) 则返回 ( exttt{calc(p%q,q,r,n,U,U^(p/q)*R)}),因为此时两个 ( ext{R}) 之间以及第一个 ( ext{R}) 之前至少有 (lfloorfrac{p}{q} floor)( ext{U}),所以可以进行合并。

(m=lfloorfrac{pn+r}{q} floor)。若 (m=0) 则返回 ( ext{R}^n),因为此时没有 ( ext{U})

其他情况,我们应当把直线对 (y=x) 做对称,再把 ( ext{U,R}) 交换,变为 (y=frac{qx-r}{p}(xin(0,m])),答案应当是一样的。但此时到整点时先记 ( ext{R}) 再记 ( ext{U}),所以可以把直线向下平移得到 (y=frac{qx-r-1}{p}),就变为了先记 ( ext{U}) 再记 ( ext{R})

但是 (r'=-r-1) 又不满足 (0le r<q) 的限制了,所以可以再把直线往左平移得到 (y=frac{qx+q-r-1}{p}),再往下平移得到 (y=frac{qx+((q-r-1)mod p)}{p}),这就满足了限制。特判 ((0,1])((m,frac{pn+r}{q}]) 的贡献。于是返回 ( exttt{U^{(q-r-1)/p}*R*calc(q,p,(q-r-1)%p,m-1,U,R)*U^{n-(m*q-r-1)/p}})

(其实往上平移和往左平移是一样的,只是为了方便理解,这一部分可能有点绕,建议自己画画图理解一下)

关于时间复杂度,这里用到了Node 的快速幂,但其实时间复杂度不超过 (O(logfrac{q}{p})=O(log q-log p))。所以总时间复杂度为 (O(Flogmax{p,q})),其中 (O(F)) 表示 Node 乘法的时间复杂度。

3. [LOJ6440] 万能欧几里得

题目描述:求

[sum_{x=1}^lA^xB^{lfloorfrac{px+r}{q} floor} mod 998244353 ]

其中 (A,B)(n imes n) 的矩阵。

数据范围:(nle 20,p,q,r,l,lfloorfrac{pl}{q} floorle 10^{18},0le A_{i,j},B_{i,j}<998244353)

这个用普通方法是做不了的,需要用上面的高级方法。

Node结构体为矩阵三元组 ((X,Y,ans)),表示 (A^{cnt1},B^{cnt2}) 和答案。于是乘法就是

[egin{aligned} &(X,Y,ans) imes(X',Y',ans') \ =&(X*X',Y*Y',ans+X*ans'*Y) end{aligned} ]

然后把上面的做法直接套用就完事了。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
typedef __int128 LLL;
const int mod = 998244353;
template<typename T>
inline void read(T &x){
	int ch = getchar(); x = 0;
	for(;ch < '0' || ch > '9';ch = getchar());
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
inline void qmo(int &x){x += (x >> 31) & mod;}
LL p, q, r, l, n;
struct Mat {
	int a[20][20];
	Mat(){memset(a, 0, sizeof a);}
	Mat operator = (const Mat &o){memcpy(a, o.a, sizeof a); return *this;}
	Mat operator + (const Mat &o) const {
		Mat res;
		for(Rint i = 0;i < n;++ i)
			for(Rint j = 0;j < n;++ j)
				qmo(res.a[i][j] = a[i][j] + o.a[i][j] - mod);
		return res;
	}
	Mat operator * (const Mat &o) const {
		Mat res;
		for(Rint i = 0;i < n;++ i)
			for(Rint k = 0;k < n;++ k)
				for(Rint j = 0;j < n;++ j)
					qmo(res.a[i][j] += (LL) a[i][k] * o.a[k][j] % mod - mod);
		return res;
	}
	void print(){
		for(Rint i = 0;i < n;++ i)
			for(Rint j = 0;j < n;++ j) printf("%d%c", a[i][j], " 
"[j == n - 1]);
	}
} A, B, I;
struct Node {
	Mat x, y, ans;
	Node(const Mat &a = I, const Mat &b = I, const Mat &c = Mat()): x(a), y(b), ans(c){}
	Node operator * (const Node &o) const {return Node(x * o.x, y * o.y, ans + x * o.ans * y);}
	void print(){ans.print();}
} aa, bb;
Mat ksm(Mat a, LL b){
	Mat res = I;
	for(;b;b >>= 1, a = a * a) if(b & 1) res = res * a;
	return res;
}
Node ksm(Node a, LL b){
	Node res;
	for(;b;b >>= 1, a = a * a) if(b & 1) res = res * a;
	return res;
}
Node calc(LL p, LL q, LL r, LL n, const Node &a, const Node &b){
	LL m = ((LLL) p * n + r) / q;
	if(!m) return ksm(b, n);
	if(p >= q) return calc(p % q, q, r, n, a, ksm(a, p / q) * b);
	return ksm(b, (q - r - 1) / p) * a * calc(q, p, (q - r - 1) % p, m - 1, b, a) * ksm(b, n - ((LLL) m * q - r - 1) / p);
}
int main(){
	read(p); read(q); read(r); read(l); read(n);
	for(Rint i = 0;i < n;++ i) I.a[i][i] = 1;
	for(Rint i = 0;i < n;++ i)
		for(Rint j = 0;j < n;++ j)
			read(A.a[i][j]);
	for(Rint i = 0;i < n;++ i)
		for(Rint j = 0;j < n;++ j)
			read(B.a[i][j]);
	aa.x = I; aa.y = B; aa.ans = Mat();
	bb.x = A; bb.y = I; bb.ans = A;
	(Node(I, ksm(B, r / q), Mat()) * calc(p, q, r % q, l, aa, bb)).print();
}

还有一道例题

原文地址:https://www.cnblogs.com/AThousandMoons/p/13129093.html