zoukankan      html  css  js  c++  java
  • [学习笔记] 类欧几里得算法

    简介

    类欧几里得算法其实是递归子问题巧妙运用的一个范例,主要用于计算下列柿子:

    [f(a,b,c,n)=sum_{i=0}^nlfloorfrac{ai+b}{c} floor ]

    [g(a,b,c,n)=sum_{i=0}^nilfloorfrac{ai+b}{c} floor ]

    [h(a,b,c,n)=sum_{i=0}^nlfloorfrac{ai+b}{c} floor^2 ]

    这个东西看上去不是很好做,有一种特殊情况,就是可以把 (a,b) 模掉 (c) 之后递归下去。如果 (a,b)(c) 小呢?那么我们就通过一些操作交换 (a,c) 的位置使得能像辗转相除那样递归下去,这样时间复杂度就是 (O(log n)) 的了。

    当然哪有说得这么简单,还是要推柿子。

    我用的是传统方法,还有一个叫万能欧几里得的高科技,我太懒了不想学

    f的推导

    如果 (ageq c) 或者 (bgeq c),我们想把它取模之后递归下去:

    [egin{aligned} f(a,b,c,n)&=sum_{i=0}^nlfloorfrac{ai+b}{c} floor\ &=sum_{i=0}^nlfloorfrac{(lfloorfrac{a}{c} floor c+amod c)i+(lfloorfrac{b}{c} floor c+bmod c)}{c} floor\ &=frac{n(n+1)}{2}lfloorfrac{a}{c} floor+(n+1)lfloorfrac{b}{c} floor+sum_{i=0}^nlfloorfrac{(amod c)i+(bmod c)}{c} floor\ &=frac{n(n+1)}{2}lfloorfrac{a}{c} floor+(n+1)lfloorfrac{b}{c} floor+f(amod c,bmod c,c,n)\ end{aligned} ]

    剩下的就是 (a<c,b<c) 的情况了,直接推肯定是转化不动的,这里有一个神来之笔,我们引入一个新的枚举变量 (j),然后交换 (i,j) 的求和顺序,把限制转化到 (i) 那里,就能找出递归子问题,看柿子理解这个思想:

    [egin{aligned} sum_{i=0}^nlfloorfrac{ai+b}{c} floor&=sum_{i=0}^nsum_{j=0}^{lfloorfrac{ai+b}{c} floor-1}1\ &=sum_{j=0}^{lfloorfrac{an+b}{c} floor-1}sum_{i=0}^n[j<lfloorfrac{ai+b}{c} floor] end{aligned} ]

    接下来要对里面的条件判断式动一下手脚了,记住我们的目的是把限制转化到 (i) 那里,所以要推 (i) 的不等式(我上面应该打等价符号,但是打不出来):

    [j<lfloorfrac{ai+b}{c} floorRightarrow j+1leqlfloorfrac{ai+b}{c} floorRightarrow j+1leqfrac{ai+b}{c} ]

    [Rightarrow jc+cleq ai+bRightarrow jc+c-b-1<aiRightarrow lfloorfrac{jc+c-b-1}{a} floor<i ]

    那么把这个限制条件代换到原式里面,设 (m=lfloorfrac{an+b}{c} floor)

    [egin{aligned} f(a,b,c,n)&=sum_{j=0}^{m-1}sum_{i=0}^n[i>lfloorfrac{jc+c-b-1}{a} floor]\ &=sum_{j=0}^{m-1}n-lfloorfrac{jc+c-b-1}{a} floor\ &=nm-f(c,c-b-1,a,m-1) end{aligned} ]

    你发现 (a,c) 交换了位置,所以复杂度就是辗转相除的 (O(log n))

    g的推导

    还是首先取模:

    [g(a,b,c,n)=g(amod c,bmod c,c,n)+lfloorfrac{a}{c} floorfrac{n(n+1)(2n+1)}{6}+lfloorfrac{b}{c} floorfrac{n(n+1)}{2} ]

    (m=lfloorfrac{an+b}{c} floor,t=lfloorfrac{jc+c-b-1}{a} floor),推导过程和上面大体是一样的:

    [egin{aligned} g(a,b,c,n)&=sum_{i=0}^nilfloorfrac{ai+b}{c} floor\ &=sum_{j=0}^{m-1}sum_{i=0}^n[j<lfloorfrac{ai+b}{c} floor]cdot i\ &=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}sum_{j=0}^{m-1}n^2+n-t-t^2\ &=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的推导

    虽然模的柿子看上去有点复杂,但其实直接暴力相乘展开就行了:

    [egin{aligned} h(a,b,c,n)&=h(amod c,bmod c,c,n)\ &+2lfloorfrac{b}{c} floor f(amod c,bmod c,c,n)+2lfloorfrac{a}{c} floor g(amod c,bmod c,c,n)\ &+lfloorfrac{a}{c} floor^2frac{n(n+1)(2n+1)}{6}+lfloorfrac{b}{c} floor^2(n+1)+lfloorfrac{a}{c} floorlfloorfrac{b}{c} floor n(n+1) end{aligned} ]

    这个平方要被表示成 (j) 的求和,要稍微操作一下:

    [n^2=(2sum_{i=0}^ni)-n ]

    应用上面这个简单的原理来推导:

    [egin{aligned} h(a,b,c,n)&=sum_{i=0}^nlfloorfrac{ai+b}{c} floor^2=sum_{i=0}^n[2sum_{j=1}^{lfloorfrac{ai+b}{c} floor}j-lfloorfrac{ai+b}{c} floor]\ &=(2sum_{i=0}^nsum_{j=1}^{lfloorfrac{ai+b}{c} floor}j)-f(a,b,c,n) end{aligned} ]

    化简前一个柿子就行了:

    [egin{aligned} sum_{i=0}^nsum_{j=1}^{lfloorfrac{ai+b}{c} floor}j&=sum_{i=0}^nsum_{j=0}^{lfloorfrac{ai+b}{c} floor-1}(j+1)\ &=sum_{j=0}^{m-1}(j+1)sum_{i=0}^n[j<lfloorfrac{ai+b}{c} floor]\ &=sum_{j=0}^{m-1}(j+1)(n-t)\ &=frac{1}{2}nm(m+1)-sum_{j=0}^{m-1}(j+1)lfloorfrac{jc+c-b-1}{a} floor\ &=frac{1}{2}nm(m+1)-g(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1) end{aligned} ]

    把上面的推导综合一下,得到了我们想要的东西:

    [h(a,b,c,n)=nm(m+1)-2g(c,c-b-1,a,m-1)-2f(c,c-b-1,a,m-1)-f(a,b,c,n) ]

    例题

    先看一道模板:类欧几里得算法

    为了方便写,可以直接三合一,一个递归解决问题。

    #include <cstdio>
    #define int long long
    const int MOD = 998244353;
    const int inv2 = (MOD+1)/2;
    const int inv6 = (MOD+1)/6;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,a,b,c;
    struct node
    {
    	int f,g,h;
    	node() {f=g=h=0;}
    };
    node work(int n,int a,int b,int c)
    {
    	int ac=a/c,bc=b/c,n1=n+1,n2=(2*n+1)%MOD,m=(a*n+b)/c;
    	node d;
    	if(!a)
    	{
    		d.f=bc*n1%MOD;
    		d.g=bc*n%MOD*n1%MOD*inv2%MOD;
    		d.h=bc*bc%MOD*n1%MOD;
    		return d;
    	}
    	if(a>=c || b>=c)
    	{
    		d.f=n*n1%MOD*inv2%MOD*ac%MOD+n1*bc%MOD;
    		d.g=ac*n%MOD*n1%MOD*n2%MOD*inv6%MOD+bc*n%MOD*n1%MOD*inv2%MOD;
    		d.h=ac*ac%MOD*n%MOD*n1%MOD*n2%MOD*inv6%MOD
    		+bc*bc%MOD*n1%MOD+ac*bc%MOD*n%MOD*n1%MOD;
    		d.f%=MOD;d.g%=MOD;d.h%=MOD;
    		node e=work(n,a%c,b%c,c);
    		d.f=(d.f+e.f)%MOD;
    		d.g=(d.g+e.g)%MOD;
    		d.h=(d.h+e.h+2*bc%MOD*e.f+2*ac%MOD*e.g)%MOD;
    		return d;
    	}
    	node e=work(m-1,c,c-b-1,a);
    	d.f=(n*m-e.f)%MOD;
    	d.g=(m*n%MOD*n1-e.h-e.f)%MOD*inv2%MOD;
    	d.h=(n*m%MOD*(m+1)-2*e.g-2*e.f-d.f)%MOD;
    	d.f=(d.f+MOD)%MOD;d.g=(d.g+MOD)%MOD;d.h=(d.h+MOD)%MOD;
    	return d;
    }
    signed main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();a=read();b=read();c=read();
    		node t=work(n,a,b,c);
    		printf("%lld %lld %lld
    ",t.f,t.h,t.g);
    	}
    }
    
  • 相关阅读:
    Changing Icon File Of Push Button At Runtime In Oracle Forms 6i
    Set Font Properties On Mouse Hover Of Push Button And Text Items At Run time In Oracle Forms
    Change An Item Property Using Set_Item_Property In Oracle Forms
    Calling / Running a report in Oracle forms 10g / 11g
    Change Or Set Report Object Property At Run Time In Oracle Forms Using Set_Report_Object_Property Command
    Refresh / Updating a form screen in Oracle D2k Forms 6i
    Know How And When To Use System.Message_Level To Control Messages In Oracle Forms
    Perform Cut Copy Paste Operations Using Cut_Region Copy_Region Paste_Region Commands In Oracle Forms
    CHECKBOX_CHECKED built-in in Oracle D2k Forms
    Limiting To Select Only 5 Check Boxes Out Of Ten In Oracle Forms
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14583868.html
Copyright © 2011-2022 走看看