同余问题+一本通题解

基本概念

同余

欧拉定理

欧拉定理推论及扩展欧拉定理

扩欧不会证(悲

费马小定理

ps:这里对费马小定理的证明需要使用欧拉定理,建议先学习欧拉定理

扩展欧几里得算法

注意:ybt上的题解写的是错误的,我的博客也被误导了,将下文中所有 \(gcd(x,y)\) 手动替换成 \(gcd(a,b)\),抱歉!

代码:

void exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    int g=y;
    y=x-(a/b)*y;
    x=g;
    return;
}

线性同余方程

乘法逆元

中国剩余定理

T1:

扩展欧几里得求解

T2:

如果 \(A\) 的质因数分解为:\(A = p_1^{Bk_1} \times p_2^{Bk_2} \times \cdots \times p_r^{Bk_r}\)
\(n\) 约数之和可以表示为:$ (1 + p_1 + p_1^2 + \cdots + p_1^{Bk_1}) \times (1 + p_2 + p_2^2 + \cdots + p_2^{Bk_2}) \times \cdots \times (1 + p_r + p_r^2 + \cdots + p_r^{Bk_r}) $
当我们把所有质因数的这些和相乘时,我们实际上是在组合所有可能的选择来形成 \(A^B\) 的约数。每一个质因数的幂次选择都是独立的,所以我们可以将它们的选择相乘来得到所有可能的约数之和。

然后对于每一个 $(1 + p_r + p_r^2 + \cdots + p_r^{Bk_r}) $,我们都可以用等比数列来求解

等比数列求和公式证明:

然后如何用根号的复杂度求一个数的质因子呢?

首先考虑一个数至多只有一个大于其根号的质因子,我们只要枚举 \(1~\sqrt n\) 之内的数,判断其是否能整除n,若可以整除,就一直除除到其不能整除n为止然后为什么能除到的都是质因子呢?因为有若一个不是质因子的数能整除n,那么必然有一个比它小的质因子也能整除n,n已经这个质因子已经被除完了,自然就不存在了

看代码更方便理解:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=9901,N=50000;
int a,b,cnt,ans=1;
int lg[N],p[N],num[N];
int quickpow(int x,int k){
    int g=1;
    lg[0]=x;
    for(int i=1;i<=30;i++){
        lg[i]=lg[i-1]*lg[i-1]%mod;
    }
    for(int i=0;i<=30;i++){
        if(!((k>>i)&1))  continue;
        g=g*lg[i]%mod;
    }
    return g;
}
signed main(){
    scanf("%lld%lld",&a,&b);
    for(int i=2;i*i<=a;i++){
        if(a%i==0){
            cnt++;
            p[cnt]=i;
            while(a%i==0){
                num[cnt]++;
                a/=i;
            }
        }
    }
    if(a>1){
        cnt++;
        p[cnt]=a;
        num[cnt]++;
    }
    for(int i=1;i<=cnt;i++){
        // printf("%lld %lld\n",p[i],num[i]);
        ans=ans*(quickpow(p[i],b*num[i]+1)%mod-1+mod)%mod*(quickpow(p[i]-1,mod-2))%mod;
    }
    printf("%lld",ans);
}

T3:

在乘法逆元中有讲,注意开long long

T4:

见上文中国剩余定理

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=15;
int n,M=1,x,y,ans;
int m[N],a[N],k[N],c[N];
void exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1,y=0;
        return;
    }
    exgcd(b,a%b,x,y);
    int g=y;
    y=x-(a/b)*y;
    x=g;
    return;
}
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&m[i],&a[i]);
        M*=m[i];
    }
    for(int i=1;i<=n;i++){
        k[i]=M/m[i];
        exgcd(k[i],m[i],x,y);
        c[i]=(x%m[i]+m[i])%m[i];//注意这里要变为最小正整数解
        ans=(ans+(a[i]*k[i]%M*c[i]%M))%M;
    }
    printf("%lld",ans);
}

T5:

经典trick,一眼秒,直接看前缀和在取模意义下相等的个数求解即可

T6:

线性同余方程

T7:

一眼秒

T8:

我们考虑当M已知时两个野人住一个洞满足存在一组野人住在同一个洞的时间在两个野人寿命之内,我们根据这个列出同余方程即可

T9:

发现一个数被取的次数为 \(C^{i-1}_{n-1}\),然后我们可以通过预处理得出, \(C^{i-1}_{n-1}=C^{i-2}_{n-1}*(n-i)/i\),但显然这一项要求逆元

这道题坑在哪里,你会发现一个数有逆元,当且仅当 \(gcd(a,b)=1\)时才存在,然而我们会发现,只要p不是质数,就必然会有几个数不存在逆元,这事情就难搞了,我也不会,没看懂题解,只拿了25分草草收尾

posted @ 2024-12-11 10:27  daydreamer_zcxnb  阅读(57)  评论(0编辑  收藏  举报