• 类欧几里得学习笔记


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

    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();
    }
    

    还有一道例题

  • 相关阅读:
    将博客搬至CSDN
    JAVA代码备注
    清空数据库SQL
    实战ASP.NET访问共享文件夹(含详细操作步骤)
    我希望我知道的七个JavaScript技巧 译(转)
    ASP.NET获取客户端网卡使用的MAC地址信息
    JS中offsetTop、clientTop、scrollTop、offsetTop各属性介绍
    JS屏幕距离参数
    jQuery插件开发精品教程,让你的jQuery提升一个台阶
    jQuery编程的最佳实践
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/13129093.html
Copyright © 2020-2023  润新知