类欧几里得算法学习笔记

偶然发现了学长发给我的一个学长的学长也是我的学长的一个数论 p p t ppt ppt,先不着急复习莫反杜教筛,按这个顺序来吧

0.随便说说

前一阵子确实学习状态不是很好,我感觉我个人学习状态也是忽好忽坏的,不过只要在学习状态好的时候多学点感觉就可以学到很多东西。
看到学长之前发给我的 p p t ppt ppt感慨也很多,也跟学长聊了聊,这些知识都是别人高中甚至初中就学会的知识点,如果高中时候好好学 O I OI OI,会不会现在有不一样的未来呢。其实我对现在也很满意了,能做一个普通学校的普通的 A C M e r ACMer ACMer就已经很满足了,起码也在热爱的路上,希望能拿点成绩出来吧。

1.随便讲讲

类欧几里得算法可以在 O ( log ⁡ n ) O(\log{n}) O(logn)的时间复杂度解决如下形式的前缀和函数
f ( a , b , c , n ) = ∑ i = 0 n ⌊ a i + b c ⌋ f(a,b,c,n)=\sum_{i=0}^n \lfloor\frac{ai+b}{c}\rfloor f(a,b,c,n)=i=0ncai+b
这个式子的向下取整让我们很容易联想到数论分块,可惜数论分块不能解决这个问题,因此我们要用到类欧几里得算法。

前置知识:几个和向上向下取整有关的不等式

ok现在开始推导,分成两种情况,第一种是 a ≥ c a\ge c ac b ≥ c b\ge c bc,第二种是 a < c a<c a<c b < c b<c b<c

可以看到,第一种情况我们直接将 a , b a,b a,b缩小到了 c c c以下,第二种情况我们交换了 a , c a,c a,c的位置,数字对 ( a , c ) (a,c) (a,c)的变换与求解 g c d ( a , c ) gcd(a,c) gcd(a,c)时的变换相同,因此时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)

看起来复杂,但由于用到的是递归的思想,所以并不难
在这里插入图片描述

牛客多校(六)C idol!!!

题面

题意就这一句话,意思是让你找 ∏ i = 1 n i ! ! \prod_{i=1}^{n}i!! i=1ni!!的尾数有多少个 0 0 0

上午刚打完模板,下午就出了个能用的题,不过这题用这个方法应该是有点降维打击,有很多比这个方法简单的方法。
首先有个性质就是 n ! ! × ( n + 1 ) ! ! = ( n + 1 ) ! n!!\times (n+1)!!=(n+1)! n!!×(n+1)!!=(n+1)!,所以题目可变为为
∏ i = 0 ⌊ n 2 ⌋ ( 2 i + 1 ) ! , 2 ∤ n , ∏ i = 0 f r a c n 2 ( 2 i ) ! , 2 ∣ n \prod_{i=0}^{\lfloor\frac{n}{2}\rfloor}(2i+1)!,2\nmid n,\prod_{i=0}^{frac{n}{2}}(2i)!,2\mid n i=02n(2i+1)!,2n,i=0fracn2(2i)!,2n
考虑题意,要求尾数有多少个 0 0 0,那么就是要找有多少个 10 , 10, 10,也就是要找有多少对 2 , 5 2,5 2,5;进一步地,有一个 5 5 5一定至少有一个 2 2 2,所以我们找 5 5 5的数量即可。
对于 n ! n! n!,其 5 5 5的个数为 ∑ i = 1 ⌊ log ⁡ 5 n ⌋ ⌊ n 5 i ⌋ \sum_{i=1}^{\lfloor\log_5n\rfloor}\lfloor\frac{n}{5^i}\rfloor i=1log5n5in
那对于 ∏ i = 0 ⌊ n 2 ⌋ ( 2 i + 1 ) ! \prod_{i=0}^{\lfloor\frac{n}{2}\rfloor}(2i+1)! i=02n(2i+1)! ∏ i = 0 n 2 ( 2 i ) ! \prod_{i=0}^{\frac{n}{2}}(2i)! i=02n(2i)! 5 5 5的个数分别为

∑ i = 0 ⌊ n 2 ⌋ ∑ i = 1 ⌊ log ⁡ 5 n ⌋ ⌊ 2 i + 1 5 i ⌋ , ∑ i = 0 ⌊ n 2 ⌋ ∑ i = 1 ⌊ log ⁡ 5 n ⌋ ⌊ 2 i 5 i ⌋ \sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\sum_{i=1}^{\lfloor\log_5n\rfloor}\lfloor\frac{2i+1}{5^i}\rfloor,\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\sum_{i=1}^{\lfloor\log_5n\rfloor}\lfloor\frac{2i}{5^i}\rfloor i=02ni=1log5n5i2i+1,i=02ni=1log5n5i2i

交换 ∑ \sum 的位置,有
∑ i = 1 ⌊ log ⁡ 5 n ⌋ ∑ i = 0 ⌊ n 2 ⌋ ⌊ 2 i + 1 5 i ⌋ , ∑ i = 1 ⌊ log ⁡ 5 n ⌋ ∑ i = 0 ⌊ n 2 ⌋ ⌊ 2 i 5 i ⌋ \sum_{i=1}^{\lfloor\log_5n\rfloor}\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\lfloor\frac{2i+1}{5^i}\rfloor,\sum_{i=1}^{\lfloor\log_5n\rfloor}\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}\lfloor\frac{2i}{5^i}\rfloor i=1log5ni=02n5i2i+1,i=1log5ni=02n5i2i
即为
∑ i = 1 ⌊ log ⁡ 5 n ⌋ f ( 2 , 1 , 5 i , ⌊ n 2 ⌋ ) , n ∤ 2 , \sum_{i=1}^{\lfloor\log_5n\rfloor}f(2,1,5^i,\lfloor\frac{n}{2}\rfloor),n\nmid 2, i=1log5nf(2,1,5i,2n⌋),n2, ∑ i = 1 ⌊ log ⁡ 5 n ⌋ f ( 2 , 0 , 5 i , n 2 ) , n ∣ 2 。 \sum_{i=1}^{\lfloor\log_5n\rfloor}f(2,0,5^i,\frac{n}{2}),n\mid 2。 i=1log5nf(2,0,5i,2n),n2

这题会炸 l o n g    l o n g long\,\,long longlong,牛客支持 i n t 128 int128 int128,找了50分钟的 i n t 128 int128 int128板子之后才知道,浪费了好久的时间

#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
typedef long long LL;
inline void read(ll &n){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    n=x*f;
}
inline void print(ll n){
    if(n<0){putchar('-');n*=-1;}
    if(n>9) print(n/10);
    putchar(n % 10 + '0');
}
ll f_gcd(ll a, ll b, ll c, ll n){
    ll res=n*(n+1)/2*(a/c)+(n+1)*(b/c);
    a%=c,b%=c;ll m=(a*n+b)/c;
    if(m==0)return res;
    return res+n*m-f_gcd(c,c-b-1,a,m-1);
}
ll n,ans;
int main(){
    read(n);
    for(ll i=1;i<=n/5;i=i*5){    
        ll now=i*5;
        if(n&1)ans+=f_gcd(2ll,1ll,now,n/2);
        else ans+=f_gcd(2ll,0ll,now,n/2);
    }
    print(ans);puts("");
    return 0;
}
AtCoder ABC283Ex Popcount Sum

题面
p o p c o u n t ( x ) = ∑ i = 0 log ⁡ 2 x ( x > > i ) & 1 = ∑ i = 0 log ⁡ 2 x ( ( x > > i ) − ( ( x > > i + 1 ) < < 1 ) ) = ∑ i = 0 log ⁡ 2 x ( ⌊ x 2 i ⌋ − 2 ⌊ x 2 i + 1 ⌋ ) popcount(x)=\sum_{i=0}^{\log_2^x}(x>>i)\&1=\sum_{i=0}^{\log_2^x}((x>>i)-((x>>i+1)<<1))=\sum_{i=0}^{\log_2^x}(\lfloor\frac{x}{2^i}\rfloor-2\lfloor\frac{x}{2^{i+1}}\rfloor) popcount(x)=i=0log2x(x>>i)&1=i=0log2x((x>>i)((x>>i+1)<<1))=i=0log2x(⌊2ix22i+1x⌋)

所以本题所求

∑ j m o d    m = r n p o p c o u n t ( j ) = ∑ j = 0 ⌊ n − r m ⌋ p o p c o u n t ( m j + r ) = ∑ j = 0 ⌊ n − r m ⌋ ∑ i = 0 log ⁡ 2 m j + r ( ⌊ m j + r 2 i ⌋ − 2 ⌊ m j + r 2 i + 1 ⌋ ) \sum_{j \mod m=r}^{n}popcount(j)=\sum_{j=0}^{\lfloor\frac{n-r}{m}\rfloor}popcount(mj+r)=\sum_{j=0}^{\lfloor\frac{n-r}{m}\rfloor}\sum_{i=0}^{\log_2^{mj+r}}(\lfloor\frac{mj+r}{2^i}\rfloor-2\lfloor\frac{mj+r}{2^{i+1}}\rfloor) jmodm=rnpopcount(j)=j=0mnrpopcount(mj+r)=j=0mnri=0log2mj+r(⌊2imj+r22i+1mj+r⌋)

= ∑ i = 0 log ⁡ 2 m j + r ∑ j = 0 ⌊ n − r m ⌋ ( ⌊ m j + r 2 i ⌋ − 2 ⌊ m j + r 2 i + 1 ⌋ ) = ∑ i = 0 log ⁡ 2 m j + r ( f ( m , r , 2 i , ⌊ n − r m ⌋ ) − 2 f ( m , r , 2 i + ! , ⌊ n − r m ⌋ ) ) =\sum_{i=0}^{\log_2^{mj+r}}\sum_{j=0}^{\lfloor\frac{n-r}{m}\rfloor}(\lfloor\frac{mj+r}{2^i}\rfloor-2\lfloor\frac{mj+r}{2^{i+1}}\rfloor)=\sum_{i=0}^{\log_2^{mj+r}}(f(m,r,2^i,\lfloor\frac{n-r}{m}\rfloor)-2f(m,r,2^{i+!},\lfloor\frac{n-r}{m}\rfloor)) =i=0log2mj+rj=0mnr(⌊2imj+r22i+1mj+r⌋)=i=0log2mj+r(f(m,r,2i,mnr⌋)2f(m,r,2i+!,mnr⌋))

前面一个 l o g log log ,计算 f f f 还有一个 l o g log log ,时间复杂度是 l o g log log 方的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline void read(ll &x){
    ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    x=s*w;
}
ll f_gcd(ll a, ll b, ll c, ll n){
    ll res=n*(n+1)/2*(a/c)+(n+1)*(b/c);
    a%=c,b%=c;ll m=(a*n+b)/c;
    if(m==0)return res;
    return res+n*m-f_gcd(c,c-b-1,a,m-1);
}
ll t,n,m,r;
int main(){
    read(t);
    while(t--){
        read(n),read(m),read(r);
        ll k=log(n)/log(2),p=1,ans=0;
        for(ll i=0;i<=k;i++){
            ans+=f_gcd(m,r,p,(n-r)/m)-2*f_gcd(m,r,(p<<1),(n-r)/m);
            p<<=1;
        }
        printf("%lld\n",ans);
    }
}

2.扩展

现在将 f ( a , b , c , n ) = ∑ i = 0 n ⌊ a i + b c ⌋ f(a,b,c,n)=\sum_{i=0}^n \lfloor\frac{ai+b}{c}\rfloor f(a,b,c,n)=i=0ncai+b扩展成其他形式的函数
g ( a , b , c , n ) = ∑ i = 0 n i ⌊ a i + b c ⌋ , h ( a , b , c , n ) = ∑ i = 0 n ⌊ a i + b c ⌋ 2 g(a,b,c,n)=\sum_{i=0}^n i \lfloor\frac{ai+b}{c}\rfloor,h(a,b,c,n)=\sum_{i=0}^n \lfloor\frac{ai+b}{c}\rfloor^2 g(a,b,c,n)=i=0nicai+b,h(a,b,c,n)=i=0ncai+b2
对于如上的两个函数,同 f f f的推导过程,我们分两种情况对 g , h g,h g,h进行推导:

g ( a , b , c , n ) : g(a,b,c,n): g(a,b,c,n):

在这里插入图片描述

h ( a , b , c , n ) : h(a,b,c,n): h(a,b,c,n):

可以发现 g ( a , b , c , n ) g(a,b,c,n) g(a,b,c,n) h ( a , b , c , n ) h(a,b,c,n) h(a,b,c,n)需要互相调用到对方的函数,因此递归的时候我们直接将 f , g , h f,g,h f,g,h三个函数一起递归即可

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

题面
打了一上午,发现自己前面推的每个函数都有问题,难绷,现在已经改正,就是一个朴素的递归。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
struct euc{
    ll f,g,h;
    euc(){f=g=h=0;}
    euc(ll x, ll y, ll z){f=x,g=y,h=z;}
};
inline ll qpow(ll x, ll y){
    ll res=1;
    while(y){
        if(y&1)(res*=x)%=mod;
        (x*=x)%=mod,y>>=1;
    }
    return res;
}
const ll inv2=qpow(2LL,mod-2);
const ll inv6=qpow(6LL,mod-2);
euc ans(ll a, ll b, ll c, ll n){
    euc res,nxt;
    if(a>=c||b>=c||a==0){
        res.f=(n*(n+1)%mod*inv2%mod*(a/c)%mod+(n+1)*(b/c)%mod)%mod;
        res.g=(n*(n+1)%mod*((n*2+1)%mod)%mod*inv6%mod*(a/c)%mod+n*(n+1)%mod*inv2%mod*(b/c)%mod)%mod;
        res.h=(n*(n+1)%mod*(a/c)%mod*(b/c)%mod+(n+1)*(b/c)%mod*(b/c)%mod)%mod;
        (res.h+=n*(n+1)%mod*(n*2+1)%mod*inv6%mod*(a/c)%mod*(a/c)%mod)%=mod;
        if(a==0)return res;
        nxt=ans(a%c,b%c,c,n);
        (res.f+=nxt.f)%=mod,(res.g+=nxt.g)%=mod;
        (res.h+=(((a/c)*2%mod*nxt.g%mod+(b/c)*2%mod*nxt.f%mod)%mod+nxt.h)%mod)%=mod;
        return res;
    }
    ll m=(a*n+b)/c;
    nxt=ans(c,c-b-1,a,m-1);
    res.f=(n*m%mod-nxt.f+mod)%mod;
    res.g=((n*m%mod*(n+1)%mod-nxt.f+mod)%mod-nxt.h+mod)%mod*inv2%mod;
    res.h=(((n*m%mod*(m+1)%mod-nxt.g*2%mod+mod)%mod-nxt.f*2%mod+mod)%mod-res.f+mod)%mod;
    return res;
}
inline ll read(){
    ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    return s*w;
}
int main(){
    ll T=read();
    while(T--){
        ll n=read(),a=read(),b=read(),c=read();
        euc now=ans(a,b,c,n);
        printf("%lld %lld %lld\n",now.f,now.h,now.g);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值