类欧几里得算法的推导

一般形式

f(a,b,c,n)=ni=0ai+bc
给定a,b,c,n,求f(a,b,c,n)
扩展:
g(a,b,c,n)=ni=0iai+bc
h(a,b,c,n)=ni=0ai+bc2

f

首先从最简单的f开始推导。

f(a,b,c,n)=ni=0ai+bc

当a≥c或b≥c时
其实就相当于把上式的分数变成 aic+bc ,然后两个变成真分数。得到:
f(a,b,c,n)=f(a % c,b % c,c,n)+n(n+1)2ac+(n+1)bc

当a < c且b < c时
m=an+bc ,上式可以表示为:
f(a,b,c,n)=ni=0mj=1[ai+bcj]
这个可以理解为一条直线在横坐标为0——n的整数时,越过第一象限的点数量。
继续推:
f(a,b,c,n)=ni=0m1j=0[ai+bcj+1]
把分数去掉
f(a,b,c,n)=ni=0m1j=0[aijc+cb]
f(a,b,c,n)=ni=0m1j=0[ai>jc+cb1]
两边除掉a
f(a,b,c,n)=ni=0m1j=0[i>jc+cb1a]
左边只有i,这样就舒服多了。下一步:
f(a,b,c,n)=m1j=0(njc+cb1a)
观察到括号里面是一个等差数列和一个f形式的求和,得到:
f(a,b,c,n)=nmf(c,cb1,a,m1)
可以发现,第1、3个系数从(a,c)变成(c,a%c)。所以就叫它类欧几里得。
边界条件:a=0

g

g(a,b,c,n)=ni=0iai+bc
首先依然是考虑a≥c或b≥c的情况。因为抽出来有一个平方和,所以得到:
g(a,b,c,n)=g(a % c,b % c,c,n)+n(n+1)(2n+1)6ac+n(n+1)2bc

当a < c且b < c时,根据f的思路,可以得到:
g(a,b,c,n)=ni=0imj=1[ai+bcj]
g(a,b,c,n)=ni=0im1j=0[i>jc+cb1a]
对于任意一个j,为了使表达式为真,i至少为 jc+cb1a+1 ,又因为上限为n,可以得到一个等差数列,所以:
g(a,b,c,n)=m1j=0(njc+cb1a)(n+jc+cb1a+1)2
拆开来得到:
g(a,b,c,n)=12m1j=0(n(n+1)jc+cb1ajc+cb1a2)
最终得到:
g(a,b,c,n)=mn(n+1)f(c,cb1,a,m1)h(c,cb1,a,m1)2

式子还和h有关,所以还要推h

h

h(a,b,c,n)=ni=0ai+bc2
当a≥c或b≥c,拆开来得到:
h(a,b,c,n)=h(a % c,b % c,c,n)+n(n+1)(2n+1)6ac2+(n+1)bc2+2bcf(a % c,b % c,c,n)+2acg(a % c,b % c,c,n)+acbcn(n+1)

当a < c且b < c时
为了方便,要把 n2 拆一下,得到: n2=2n(n+1)2n=2ni=0in
可以得到:
h(a,b,c,n)=ni=02ai+bcj=1jai+bc
把两部分分开,得到:
h(a,b,c,n)=2m1j=0(j+1)ni=0[ai+bcj+1]f(a,b,c,n)
中间的按照上面的思路推吧,最终得到:
h(a,b,c,n)=m(m+1)n2g(c,cb1,a,m1)2f(c,cb1,a,m1)f(a,b,c,n)

一道求g裸题的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int mo=1e9+7,inv2=500000004,inv6=166666668;

typedef long long LL;

int a,b,c,l,r;

struct data
{
    int f,g,h;
};

data calc(int a,int b,int c,LL n)
{
    data tmp;
    if (!a)
    {
        tmp.f=tmp.g=tmp.h=0;
        return tmp;
    }
    if (a>=c || b>=c)
    {
        tmp=calc(a%c,b%c,c,n);
        n%=mo;
        tmp.h=(tmp.h+n*(n+1)%mo*(2*n+1)%mo*inv6%mo*(a/c)%mo*(a/c)%mo
        +(n+1)*(b/c)%mo*(b/c)%mo
        +(LL)2*(a/c)*tmp.g%mo
        +(LL)2*(b/c)*tmp.f%mo
        +n*(n+1)%mo*(a/c)%mo*(b/c))%mo;
        tmp.f=(tmp.f+n*(n+1)/2%mo*(a/c)+(n+1)*(b/c))%mo;
        tmp.g=(tmp.g+n*(n+1)%mo*(2*n+1)%mo*inv6%mo*(a/c)+n*(n+1)/2%mo*(b/c))%mo;
        return tmp;
    }
    LL m=((LL)a*n+b)/c;
    data nxt=calc(c,c-b-1,a,m-1);
    n%=mo; m%=mo;
    tmp.f=((n*m-nxt.f)%mo+mo)%mo;
    tmp.g=(LL)((n*(n+1)%mo*m-nxt.f-nxt.h)%mo+mo)*inv2%mo;
    tmp.h=((m*(m+1)%mo*n-(LL)2*(nxt.g+nxt.f)%mo-tmp.f)%mo+mo)%mo;
    return tmp;
}

int main()
{
    freopen("task.in","r",stdin); freopen("task.out","w",stdout);
    scanf("%d%d%d%d%d",&a,&c,&b,&l,&r);
    printf("%d\n",(calc(a,b,c,r).g-calc(a,b,c,l-1).g+mo)%mo);
    return 0;
}
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值