zoukankan      html  css  js  c++  java
  • 类欧几里得算法小结

    基础版

    类欧几里得算法被用来处理下面这种式子的值

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

    求值的话我们考虑分类讨论

    (1) 若(ageq c)(b geq c)
    注意到(lfloor frac{ac}{b} floor=lfloor frac{(a mod b)*c}{b} floor+clfloor frac{a}{b} floor),在(ageq c)(bgeq c)时,由于(lfloorfrac{a}{c} floor)(lfloorfrac{b}{c} floorgeq 1),所以利用上式化简可以得到

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

    (2)若(a<c)(b<c)
    此时使用上面的式子化简就变得没有意义了,我们需要考虑点其它的东西

    考虑我们所计算的式子的实际意义:一条方程为(ax+cy+b=0)的直线,在这条直线下方且满足(xgeq 0),(y>0)的整点的个数,于是我们可以枚举(y)来进行化简
    (m=lfloor frac{an+b}{c} floor)

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

    注意到下取整遇到大于号时可以直接丢掉,化简一下逻辑判断式

    [egin{aligned} ai+b&geq jc+c\ i&geqfrac{jc+c-b}{a}\ i&>lfloor frac{jc+c-b-1}{a} floor end{aligned} ]

    带回原式

    [egin{aligned} f(a,b,c,n)=&sum_{i=0}^nsum_{j=0}^{m-1}[i>lfloorfrac{jc+c-b-1}{a} floor]\ =&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)\ =&n*m-f(c,c-b-1,a,m-1) end{aligned} ]

    这样我们就得到了两个十分重要的递推式,注意到它的形式类似于欧几里得算法求(gcd),所以被称作类欧几里得算法,时间复杂度和欧几里得算法几乎一致

    接下来就是边界条件,这里的边界条件我写的比较多

    (1)若(a=0),则(f(a,b,c,n)=(n+1)lfloorfrac{b}{c} floor)

    (2)若(n=0),则(f(a,b,c,n)=lfloorfrac{b}{c} floor)

    (3)若(n=1),则(f(a,b,c,n)=lfloorfrac{a+b}{c} floor+lfloorfrac{b}{c} floor)

    (4)若(c<0),则(f(a,b,c,n)=f(-a,-b,-c,n))

    (5)若(a<0)(b<0),利用最上面的第一条式子将(a,b)取正即可,即(f(a,b,c,n)=f(a mod c+c,b mod c+c,c,n)+frac{n(n+1)}{2}(lfloorfrac{a}{c} floor-1)+(n+1)(lfloorfrac{b}{c} floor-1))

    反正最后一般就是用到(a=0)的情况了

    ll work(ll a,ll b,ll c,ll n)
    {
        if (!a) return (n+1)*(b/c);
        if (!n) return b/c;
        if (n==1) return (a+b)/c+b/c;
        if (c<0) return work(-a,-b,-c,n);
        ll d=abs(gcd(gcd(a,b),c));
        a/=d;b/=d;c/=d;
        if ((a>=c) || (b>=c)) return work(a%c,b%c,c,n)+n*(n+1)/2*(a/c)+(n+1)*(b/c);
        if ((a<0) || (b<0)) return work(a%c+c,b%c+c,c,n)+n*(n+1)/2*(a/c-1)+(n+1)*(b/c-1);
        ll m=(a*n+b)/c;
        return n*m-work(c,c-b-1,a,m-1);
    }
    

    扩展版

    扩展版主要是两个式子

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

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

    这两个式子的求解是互相关联的,这一点将在下面的推导过程中体现出来,并且它们的推导和(f)有很大的相似之处

    (g)

    (g(a,b,c,n))较为简单,我们先推导它

    (1)若(ageq c)(bgeq c)
    和上面一样的化简,注意到此时(lfloorfrac{ai}{c} floor)的前面还有一个系数(i),因此按照上面的展开的话得到的系数是一个平方和,同样的(lfloorfrac{b}{c} floor)的系数也由常数(1)变成了等差数列求和,这里就直接给出结果

    [g(a,b,c,n)=g(a mod c,b mod c,c,n)+frac{n(n+1)(2n+1)}{6}lfloorfrac{a}{c} floor+frac{n(n+1)}{2}lfloorfrac{b}{c} floor ]

    (2) 若(a<c)(b<c)

    继续将(lfloorfrac{ai+b}{c} floor)展开

    [egin{aligned} g(a,b,c,n)=&sum_{i=0}^nisum_{j=1}^m[lfloorfrac{ai+b}{c} floorgeq j]\ =&sum_{i=0}^nisum_{j=0}^{m-1}[lfloorfrac{ai+b}{c} floorgeq j+1]\ =&sum_{i=0}^nisum_{j=0}^{m-1}[i>frac{jc+c-b-1}{a}]\ =&sum_{j=0}^{m-1}sum_{i=0}^ni[i>frac{jc+c-b-1}{a}] end{aligned} ]

    为了满足最后的逻辑表达式,(i)的取值在([lfloorfrac{jc+c-b-1}{a} floor+1,n])之中,所以这也是个等差数列,利用求和公式继续化简

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

    好了我们做完了

    个鬼啊,那个(h)是什么啊

    (h)

    依然是按照上面的思路分类讨论

    (1)若(ageq c)(bgeq c)
    ((a+b+c)^2=a^2+b^2+c^2+2ab+2ac+2bc)可得

    [egin{aligned} h(a,b,c,n)=&sum_{i=0}^n(lfloorfrac{a mod c*i+b mod c}{c} floor+ilfloorfrac{a}{c} floor+lfloorfrac{b}{c} floor)^2\ =&h(a mod c,b mod c,c,n)+frac{n(n+1)(2n+1)}{6}lfloorfrac{a}{c} floor^2+(n+1)lfloorfrac{b}{c} floor^2+2lfloorfrac{a}{c} floor g(a mod c,b mod c,c,n)+2lfloorfrac{b}{c} floor f(a mod c,b mod c,c,n)+n(n+1)lfloorfrac{a}{c} floorlfloorfrac{b}{c} floor end{aligned} ]

    (2)若(a<c)(b<c)

    首先给出一个变形

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

    于是

    [egin{aligned} h(a,b,c,d)=&sum_{i=0}^n(2sum_{j=1}^{lfloorfrac{ai+b}{c} floor}j-lfloorfrac{ai+b}{c} floor)\ =&2sum_{j=0}^{m-1}(j+1)sum_{i=0}^n[lfloorfrac{ai+b}{c} floorgeq j+1]-f(a,b,c,n)\ =&2sum_{j=0}^{m-1}(j+1)sum_{i=0}^n[igeq lfloorfrac{jc+c-b-1}{a} floor]-f(a,b,c,n)\ =&2sum_{j=0}^{m-1}(j+1)(n-lfloorfrac{jc+c-b-1}{a} floor)-f(a,b,c,d)\ =&2nsum_{j=0}^{m-1}(j+1)-2sum_{j=0}^{m-1}jlfloorfrac{jc+c-b-1}{a} floor-2sum_{j=0}^{m-1}lfloorfrac{jc+c-b-1}{a} floor-f(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) end{aligned} ]

    好了这回真的做完了

    注意到我们需要在(f,g,h)之间交替求值,我们对每一对((a,b,c,n))存下它的(f,g,h)值即可

    node solve(ll a,ll b,ll c,ll n)
    {
        node ans;
        if (!a)
        {
            ans.f=(n+1)*(b/c)%maxd;
            ans.g=(n+1)*n%maxd*inv2%maxd*(b/c)%maxd;
            ans.h=(n+1)*(b/c)%maxd*(b/c)%maxd;
            return ans;
        }
        if ((a>=c) || (b>=c))
        {
            node tmp=solve(a%c,b%c,c,n);
            ans.f=(tmp.f+n*(n+1)%maxd*inv2%maxd*(a/c)%maxd+(n+1)*(b/c)%maxd)%maxd;
            ans.g=(tmp.g+n*(n+1)%maxd*(n*2+1)%maxd*inv6%maxd*(a/c)%maxd+n*(n+1)%maxd*inv2%maxd*(b/c)%maxd)%maxd;
            ans.h=(tmp.h+n*(n+1)%maxd*(n*2+1)%maxd*inv6%maxd*(a/c)%maxd*(a/c)%maxd+(n+1)%maxd*(b/c)%maxd*(b/c)%maxd+tmp.f*(b/c)%maxd*2%maxd+tmp.g*(a/c)%maxd*2%maxd+(a/c)*(b/c)%maxd*n%maxd*(n+1)%maxd)%maxd;
            return ans;
        }
        ll m=(a*n+b)/c;
        node tmp=solve(c,c-b-1,a,m-1);m%=maxd;
        ans.f=(n*m%maxd+maxd-tmp.f)%maxd;
        ans.g=(m*n%maxd*(n+1)%maxd+maxd-tmp.f+maxd-tmp.h)%maxd*inv2%maxd;
        ans.h=(n*m%maxd*(m+1)%maxd-tmp.g*2%maxd-tmp.f*2%maxd-ans.f)%maxd;
        return ans;
    }
    

    记得在最后ans.f=(ans.f+maxd)%maxd;ans.g=(ans.g+maxd)%maxd;ans.h=(ans.h+maxd)%maxd; 强制转为正数即可

  • 相关阅读:
    《Redis 垃圾回收》
    python-Day3-set 集合-counter计数器-默认字典(defaultdict) -可命名元组(namedtuple)-有序字典(orderedDict)-双向队列(deque)--Queue单项队列--深浅拷贝---函数参数
    Python--day4
    python--day2
    Python--day1
    Python字符串原理剖析------万恶的+号
    执行脚本传入参数
    字符编码
    css3动画与js动画的区别
    关于FormData及其用法
  • 原文地址:https://www.cnblogs.com/encodetalker/p/11037506.html
Copyright © 2011-2022 走看看