类欧几里得算法

基础

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

其中,(a,b,c,n) 为常数,如何 (O(log n)) 求解。

(ageq c) 或者 (bgeq c) ,可以将 (a,b)(c) 取模以简化问题:

[egin{align} f(a,b,c,n) &= sum_{i=0}^{n}{leftlfloor frac{ai+b}{c} ight floor}\ &=sum_{i=0}^{n}{leftlfloor frac{(lfloor frac{a}{c} floor c+a mod c)i+(lfloor frac{b}{c} floor c+b mod c)}{c} ight floor}\ &=frac{(1+n)n}{2}·leftlfloor frac{a}{c} ight floor+(n+1)·leftlfloor frac{b}{c} ight floor+sum_{i=0}^{n}{ leftlfloor frac{(a mod c)i+(b mod c)}{c} ight floor}\ &=frac{(1+n)n}{2}·leftlfloor frac{a}{c} ight floor+(n+1)·leftlfloor frac{b}{c} ight floor+f(a mod c ,b mod c,c,n)\ end{align} ]

那么,问题就转化为 (a<c,b<c) 的情况了。观察式子,发现只有 (i) 这个变量,需要从 (i) 下手推。在推求和式子中一个常见的技巧:条件和贡献的放缩与转化。具体而言,在原式中,(0leq i leq n) 是条件,而 (leftlfloor frac{ai+b}{c} ight floor) 是对总和的贡献。

要加快一个求和式的计算过程,关键在于贡献合并计算。但这个式子的贡献并不好合并,因此考虑转化,得到另一个形式的和式:

[f(a,b,c,n) = sum_{i=0}^{n}{leftlfloor frac{ai+b}{c} ight floor}=sum_{i=0}^{n}{sum_{j=0}^{leftlfloor frac{ai+b}{c} ight floor-1}{1}} ]

现在增加了一个变量 (j) ,既然不好算 (i) 的贡献,就想办法计算 (j) 的贡献。为了得到一个和 (j) 有关的贡献式,采用数论中常用的变换方法:限制转移,即交换 (i,j) 的求和算子,用 (n) 限制 (j) 的上界。

[Rightarrow sum_{j=0}^{leftlfloor frac{an+b}{c} ight floor-1}{sum_{i=0}^{n}{left[j<leftlfloor frac{ai+b}{c} ight floor ight]}} ]

这样做的目的是让 (j) 摆脱 (i) 的限制,现在 (i,j) 都被 (n) 限制。接着,对贡献式进行一些放缩处理。

先将取整符号拿掉:

[j< leftlfloor frac{ai+b}{c} ight floor Leftrightarrow j+1 leq leftlfloor frac{ai+b}{c} ight floor Leftrightarrow j+1 leq frac{ai+b}{c} ]

再做一些变换:

[j+1 leq frac{ai+b}{c} Leftrightarrow (j+1)c leq ai+b Leftrightarrow jc+c-b-1 < ai ]

最后一步,向下取整:

[jc+c-b-1 < ai Leftrightarrow leftlfloor frac{jc+c-b-1}{a} ight floor <i ]

这一步的重要意义在于把变量 (i) 消掉了,令 (m=leftlfloor frac{an+b}{c} ight floor) ,那么原式可以化为:

[egin{align} f(a,b,c,n) &= sum_{j=0}^{m-1}{sum_{i=0}^{n}{left[ i>leftlfloor frac{jc+c-b-1}{a} ight floor ight]}}\ &= sum_{j=0}^{m-1}{left(n-leftlfloor frac{jc+c-b-1}{a} ight floor ight)}\ &= nm-sum_{j=0}^{m-1}{leftlfloor frac{jc+c-b-1}{a} ight floor}\ &= nm-f(c,c-b-1,a,m-1)\ &= nleftlfloor frac{an+b}{c} ight floor-f(c,c-b-1,a,leftlfloor frac{an+b}{c} ight floor-1) end{align} ]

可以发现,这是一个递归的式子。边界是:

  • (n=0) 时,直接返回 (lfloor frac{b}{c} floor)
  • (a=0) 时,返回 ((n+1) imeslfloor frac{b}{c} floor)

综上所述

[f(a,b,c,n)= egin{cases} lfloor frac{b}{c} floor & n=0\ (n+1) imes lfloor frac{b}{c} floor & a=0\ frac{(1+n)·n}{2} imes leftlfloor frac{a}{c} ight floor+(n+1) imes leftlfloor frac{b}{c} ight floor+f(a \% c ,b\%c,c,n) & ageq c or bgeq c\ n imes leftlfloor frac{an+b}{c} ight floor-f(c,c-b-1,a,leftlfloor frac{an+b}{c} ight floor-1) & a<c and b<c end{cases} ]

代码:

ll getSum(ll x)
{
    return x*(x+1)/2;
}
ll f(ll a,ll b,ll c,ll n)
{
    if(!n) return b/c;
    if(!a) return (n+1)*(b/c);
    if(a>=c||b>=c) return getSum(n)*(a/c)+(n+1)*(b/c)+f(a%c,b%c,c,n);
    return n*((a*n+b)/c)-f(c,c-b-1,a,(a*n+b)/c-1);
}

例题

AtCoder Regular Contest 111 E - Simple Math 3

https://atcoder.jp/contests/arc111/tasks/arc111_e

拓展

求:

[g(a,b,c,n)=sum_{i=0}^{n}{ileftlfloor frac{ai+b}{c} ight floor}\ h(a,b,c,n)=sum_{i=0}^{n}{leftlfloor frac{ai+b}{c} ight floor^2} ]

g 函数求解

首先,考虑取模:

[g(a,b,c,n)=g(a\%c,b\%c,c,n)+frac{n(n+1)(2n+1)}{6} leftlfloor frac{a}{c} ight floor+frac{n(n+1)}{2}leftlfloor frac{b}{c} ight floor ]

接下来,考虑 (a<c,b<c) 的情况,令 (m=leftlfloor frac{an+b}{c} ight floor),有:

[g(a,b,c,n)=sum_{i=0}^{n}{ileftlfloor frac{ai+b}{c} ight floor}=sum_{j=0}^{m-1}{sum_{i=0}^{n}{i·left[j<leftlfloor frac{ai+b}{c} ight floor ight]}} ]

(t=leftlfloor frac{jc+c-b-1}{a} ight floor),同理有:

[egin{align} &= sum_{j=0}^{m-1}{sum_{i=0}^{n}{[i>t]·i}}\ &= sum_{j=0}^{m-1}{frac{1}{2}(t+1+n)(n-t)}\ &= frac{1}{2} left(mn(n+1)-sum_{j=0}^{m-1}{t^2}-sum_{j=0}^{m-1}{t} ight)\ &= frac{1}{2} left(mn(n+1)-h(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1) ight) end{align} ]

综上所述

[g(a,b,c,n)=egin{cases} 0 & n=0\ frac{n(n+1)}{2} imes leftlfloor frac{b}{c} ight floor & a=0\ g(a\%c,b\%c,c,n)+frac{n(n+1)(2n+1)}{6} leftlfloor frac{a}{c} ight floor+frac{n(n+1)}{2}leftlfloor frac{b}{c} ight floor & ageq c or bgeq c \ frac{1}{2} left(mn(n+1)-h(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1) ight) & a<c and b<c\ end{cases} ]

h 函数求解

首先取模:

[egin{align} h(a,b,c,n) &= h(a\%c,b\%c,c,n)\ &+ 2 leftlfloor frac{b}{c} ight floor f(a\%c,b\%c,c,n)+2 leftlfloor frac{a}{c} ight floor g(a\%c,b\%c,c,n)\ &+ leftlfloor frac{a}{c} ight floor^2 frac{n(n+1)(2n+1)}{6}+leftlfloor frac{b}{c} ight floor^2 (n+1)+leftlfloor frac{a}{c} ight floor leftlfloor frac{b}{c} ight floor n(n+1) end{align} ]

考虑 (a<c,b<c) 的情况,令 (m=leftlfloor frac{ai+b}{c} ight floor)(t=leftlfloor frac{jc+c-b-1}{a} ight floor)

由于平方不好处理,可以按照下列方式拆分:

[n^2=2·frac{n(n+1)}{2}-n=left(2sum_{i=0}^{n}{i} ight)-n ]

这样做的意义在于,在添加变量 (j) 时只会变成一个求和算子,不会出现 (sum imes sum) 的形式。

[egin{align} h(a,b,c,n) &= sum_{i=0}^{n}{leftlfloor frac{ai+b}{c} ight floor^2}\ &= sum_{i=0}^{n}{left[left(2sum_{j=0}^{leftlfloor frac{ai+b}{c} ight floor}{j} ight)-leftlfloor frac{ai+b}{c} ight floor ight]}\ &= left(2sum_{i=0}^{n}{sum_{j=0}^{leftlfloor frac{ai+b}{c} ight floor}{j}} ight)-f(a,b,c,n)\ end{align} ]

接下来化简前一部分:

[egin{align} & sum_{i=0}^{n}{sum_{j=0}^{leftlfloor frac{ai+b}{c} ight floor}{j}}\ =& sum_{i=0}^{n}{sum_{j=0}^{leftlfloor frac{ai+b}{c} ight floor-1}{(j+1)}}\ =& sum_{j=0}^{m-1}{(j+1)sum_{i=0}^{n}{left[j<leftlfloorfrac{ai+b}{c} ight floor ight]}}\ =& sum_{j=0}^{m-1}{(j+1)sum_{i=0}^{n}{left[i>t ight]}}\ =& sum_{j=0}^{m-1}{(j+1)(n-t)}\ =& frac{1}{2}nm(m+1)-sum_{j=0}^{m-1}{(j+1)leftlfloor frac{jc+c-b-1}{a} ight floor}\ =& frac{1}{2}nm(m+1)-g(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1) end{align} ]

因此:

[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) ]

综上所述:

[h(a,b,c,n)= egin{cases} (frac{b}{c})^2 &n=0\ (n+1) imes(frac{b}{c})^2 &a=0\ h(a\%c,b\%c,c,n)+ 2 leftlfloor frac{b}{c} ight floor f(a\%c,b\%c,c,n)+2 leftlfloor frac{a}{c} ight floor g(a\%c,b\%c,c,n)+\ leftlfloor frac{a}{c} ight floor^2 frac{n(n+1)(2n+1)}{6}+leftlfloor frac{b}{c} ight floor^2 (n+1)+leftlfloor frac{a}{c} ight floor leftlfloor frac{b}{c} ight floor n(n+1) & ageq c or bgeq c \ nm(m+1)-2g(c,c-b-1,a,m-1)-2f(c,c-b-1,a,m-1)-f(a,b,c,n) & a<c and b<c \ end{cases} ]

在代码实现时,(3) 个函数会有交错递归,可以考虑 (3) 个一起整体递归,同步计算,否则会有很多项被多次计算,复杂度 (O(log n))

模板题

P5170 【模板】类欧几里得算法

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int mod=998244353;
int inv2=499122177,inv6=166374059;//mod下的2,6的逆元
struct data
{
    data()
    {
        f=g=h=0;
    }
    ll f,g,h;
};
data calc(ll a,ll b,ll c,ll n)
{
    ll ac=a/c,bc=b/c,m=(a*n+b)/c,nx=n+1,ny=2*n+1;
    data d;
    if(a==0)
    {
        d.f=bc*nx%mod;
        d.g=bc*n%mod*nx%mod*inv2%mod;
        d.h=bc*bc%mod*nx%mod;
        return d;
    }
    if(a>=c||b>=c)
    {
        d.f=n*nx%mod*inv2%mod*ac%mod+bc*nx%mod;
        d.g=ac*n%mod*nx%mod*ny%mod*inv6%mod+bc*n%mod*nx%mod*inv2%mod;
        d.h=ac*ac%mod*n%mod*nx%mod*ny%mod*inv6%mod+bc*bc%mod*nx%mod+ac*bc%mod*n%mod*nx%mod;
        d.f%=mod,d.g%=mod,d.h%=mod;
        data e=calc(a%c,b%c,c,n);// 迭代
        d.h+=e.h+2*bc%mod*e.f%mod+2*ac%mod*e.g%mod;
        d.g+=e.g,d.f+=e.f;
        d.f%=mod,d.g%=mod,d.h%=mod;
        return d;
    }
    data e=calc(c,c-b-1,a,m-1);
    d.f=n*m%mod-e.f,d.f=(d.f%mod+mod)%mod;
    d.g=m*n%mod*nx%mod-e.h-e.f,d.g=(d.g*inv2%mod+mod)%mod;
    d.h=n*m%mod*(m+1)%mod-2*e.g-2*e.f-d.f;
    d.h=(d.h%mod+mod)%mod;
    return d;
}
int main()
{
    int t;
    ll a,b,c,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
        data ans=calc(a,b,c,n);
        printf("%lld %lld %lld
",ans.f,ans.h,ans.g);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/1024-xzx/p/14339141.html