2021寒假集训

Day1:

埃氏筛

void sushu(int x){
    for(int i=2;i<=x;i++){
        if(!bj[i]){
            prime[cnt]=i;
            cnt++;
            for(int j=2;j<=x/i;j++) bj[i*j]=1;
        } 
    }
    for(int i=0;i<cnt;i++)  printf("%d ",prime [i]);
    printf("\n%d\n",cnt);
}

线性筛

void sushu(int x){
    for(int i=2;i<=x;i++){
        if(!bj[i]){
            prime[cnt]=i;
            cnt++;
        }
        for(int j=0;prime[j]<=x/i;j++){
            bj[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
    printf("%d\n",cnt);
}

欧拉函数

1 ~ N 中与 N 互质的数的个数被称为欧拉函数,记为ϕ(N)。
若在算数基本定理中,N=p1^r1 * p2^r2 * p3^r3 * … * pk^rk
φ(N)=N(1-1/P1)(1-1/P2)…(1-1/Pk)

证明如下:

∵ p1,p2,p3…pk 均为N的质因子
∴ 在 1 ~ N 中p1,p2,p3…pk的倍数均不可能与N互质
以p1为例 在1 ~ N 中p1的倍数有N/p1个
∴ ϕ(N) = N-N/p1-N/p2-…-N/pk
但是如果有一个数既是p1的倍数也是p2的倍数呢,那么在上式中就重复减了两次,所以我们需要进一步精确一下这个式子
ϕ(N) = N-N/p1-N/p2-…-N/pk+N/p1p2+N/p1p3+…
同理也会出现三个质因子的公倍数,…,k个质因子的公倍数
ϕ(N) = N-N/p1-N/p2-…-N/pk+N/p1p2+N/p1p3+…-N/p1p2p3-N/p1p2p4-…+N/p1p2p3p4…(容斥原理)
化简之后就可以得到 φ(N)=N(1-1/P1)(1-1/P2)…(1-1/Pk)

线性筛求欧拉函数
void ol(int n){
    euler[1] = 1;
    for(int i=2;i<=n;i++){
        if(!bj[i]){
            prime[cnt]=i;
            cnt++;
            euler[i]=i-1;//情况一
        }
        for(int j=0;primes[j]<=n/i;j++){
            int t=primes[j]*i;
            bj[t]=1;
            if(i%primes[j]= =0){
                euler[t]=euler[i]*primes[j];//情况二
                break;
            }
            euler[t]=euler[i]*(primes[j]-1);//情况三
        }
    }
}
情况一:i为质数 euler[i]=i-1
情况二:i%primes[j]= =0

∵ prime[j]为i的质因数
∴ t=prime[j] * i分解质因数后的结果zys(t)=zys(i),并没有发生变化
euler[t]=t * (1-1/P1)(1-1/P2)…(1-1/Pk)
euler[i]=i * (1-1/P1)(1-1/P2)…(1-1/Pk)
∵ t=prime[j] * i
∴ euler[t]=euler[i]*primes[j]

情况三:i%prime[j]!=0

t=prime[j] * i
∵prime[j]为t的质因数且不为i的质因数
∴ t分解质因数后的结果zys(t)=prime[j] * zys(i)
euler[t]=t * (1-1/P1)(1-1/P2)…(1-1/Pk)(1-1/prime[j])
euler[i]=i * (1-1/P1)(1-1/P2)…(1-1/Pk)
∵ t=prime[j] * i
∴ euler[t]=euler[i] * primes[j] * (1-1/prime[j])
∴ euler[t]=euler[i] * (primes[j]-1);

欧拉定理

若a与n互质,则a^φ(n)≡1 (mod n)

费马小定理

满足欧拉定理条件且n为质数,则a^(n-1)≡1 (mod n)

Day2:

快速幂

//a^b%p
void ksm(int a,int b,int p){
    long long int ans=1,x=a;
    while(b){
        if(b&1) ans=ans*x%p;
        x=x*x%p;
        b>>=1;
    }
    printf("%lld\n",ans);
}

快速幂求逆元

乘法逆元的定义
若整数b,m互质,并且对于任意的整数 a,如果满足b|a(b能被a整除),则存在一个整数x,使得a/b≡a∗x(mod m),则称x为b的模m乘法逆元,记为b^(−1)(mod m)。

性质:b * b^(-1) ≡ 1 (mod m)

b存在乘法逆元的充要条件是b与模数m互质。
∴模数m为质数时
满足b^(m-1) ≡ 1 (mod m)(费马小定理)
∴ b * b^(m-2) ≡ 1 (mod m)
b^(m−2)即为b的乘法逆元。

例:p是质数,求a模p的乘法逆元,若逆元不存在则输出impossible

void ny(int a,int b,int p){
    long long int ans=1,x=a;
    while(b){
        if(b&1) ans=ans*x%p;
        x=x*x%p;
        b>>=1;
    }
    if(a%p==0) printf("impossible\n");
    else printf("%lld\n",ans);
}

int main(){
    int n,a,p;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d %d",&a,&p);
        ny(a,p-2,p);
    }
    return 0;
}

gcd

求最大公约数,利用辗转相除法

int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}

裴蜀定理

(贝祖定理)

若a,b是整数,且gcd(a,b)=d,那么对于任意的非零整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。

推论:a,b互质的充要条件是存在整数x,y使ax+by=1.

证明:
∵a,b均为gcd(a,b)的整数倍
且x,y均为整数
∴ax,by均为gcd(a,b)的整数倍
∴ax+by也为gcd(a,b)的整数倍
∴gcd(a,b)是ax+by能得出的最小值

exgcd

已知整数a,b,扩展欧几里得算法可以在求得a,b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足ax+by=gcd(a,b)(裴蜀定理)

如果a是负数,可以把问题转化成|a|(-x)+by=gcd(a,b),然后令X=-x,则|a|X+by=gcd(a,b)

推导:

1.当a,b中存在0时
∵b=0
∴gcd(a,b)=a
ax+by=gcd(a,b)=a
∴x=1 y=0

2.当a,b均不为零时
∵ gcd(a,b)=gcd(b,a mod b)
∴ ax+by=gcd(a,b)=gcd(b,a mod b)=bY+(a mod b)X
a mod b=a - a/b (向下取整) * b
化简可得 gcd(a,b)=aX+b(Y-a/b (向下取整) * X)
即X=X,Y-=a/b * X;

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

ax+by=d x,y不唯一
x=x0+b/d * k,y=y0-a/d* k,k∈Z

exgcd求逆元

例:线性同余方程
给定整数a,b,m,求出整数x,使其满足a∗x≡b(mod m),如果无解则输出impossible。

∵ a∗x≡b(mod m)
设 ax=my+b 即 ax-my=b
令 Y=-y
∴ax+mY=b
由裴蜀定理可知 ax+by=gcd(a,m)
若b%gcd(a,m)= =0 则有解,反之无解
利用扩展欧几里得算法求出x0,y0
满足a∗x0+m∗y0=gcd(a,m)
(a∗x0+m∗y0)∗(b/gcd(a,m))=gcd(a,m)∗(b/gcd(a,m))
a∗x0∗b/gcd(a,m)+m∗y0∗b/gcd(a,m)=b
x=x0∗b/gcd(a,m)%m即为所求

题目

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,a,b,x,y,m;
int exgcd(int a,int b,int &x,int &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    else{
        int d=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return d;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d %d %d",&a,&b,&m);
        int d=exgcd(a,m,x,y);
        if(b%d) printf("impossible\n");
        else printf("%d\n",(LL)x*b/d%m);
    }
    return 0;
}

Day3:

约数

N=p1^r1 * p2^r2 * p3^r3 * … * pk^rk

N的约数个数 (r1+1)(r2+1)(r3+1)…(rk+1)

N的约数之和 (p10+p11+p12+…+p1r1)…(pk0+pk1+pk2+…+pkrk)

中国剩余定理

给定m1,m2,m3…mn两两互质
x ≡ a1(mod m1)
x ≡ a2(mod m2)

x ≡ ak(mod mk)

令M=m1m2m3…mk
Mi=M/mi
Mi^(-1)表示Mi模mi的逆
即Mi^(-1) * Mi ≡ 1(mod mi)

x=a1 * M1 * M1^(-1)+a2 * M2 * M2^(-1)+…+ak * Mk * Mk^(-1)

证明:(反证法)
假设x=a1 * M1 * M1^(-1)+a2 * M2 * M2^(-1)+…+ak * Mk * Mk^(-1)
等式两边同时mod m1
x mod m1 = a1 * M1 * M1^(-1)mod m1+a2 * M2 * M2^(-1)mod m1+…+ak * Mk * Mk^(-1)mod m1
∵ M1^(-1) * M1 ≡ 1(mod m1)
∴ 原式 = a1 * 1+a2 * M2 * M2^(-1)mod m1+…+ak * Mk * Mk^(-1)mod m1
又∵M2=m1m3m4…mk
∴ M2/m1=m3m4m5…mk
∴ M2 ≡ 0(mod m2)
同理 M3,M4,…Mk模m1均为0
∴ 原式 = a1 * 1+a2 * 0+…+ak * 0
∴ x mod m1 = a1满足题目要求
同理可证其余线性方程均满足要求,故假设成立

模板题

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=20;
LL m[N],a[N],M[N],M_[N],ans;
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        x=1,y=0;
        return a;
    }
    else{
        LL d=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return d;
    }
}
int  main(){
    int n;
    LL MM=1,ans=0,x,y;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld %lld",&m[i],&a[i]);
        MM*=m[i];
    }
    for(int i=1;i<=n;i++){
        M[i]=MM/m[i];
        LL d=exgcd(M[i],m[i],x,y);
        M_[i]=x/d%m[i];
        ans+=a[i]*M[i]*M_[i]%MM;
        ans%=MM;
    }
    ans=(ans+MM)%MM;
    printf("%lld\n",ans);
    return 0;
}

(扩展)中国剩余定理

x ≡ a1(mod m1)
x ≡ a2(mod m2)

x ≡ ak(mod mk)
m1,m2,m3…mn不一定两两互质,求最小正整数x

中国剩余定理有一个很重要的前提条件,就是m1,m2,m3…mn一定两两互质,但本题不满足要求,所以不能套用x的公式

∵ x ≡ a1(mod m1),x ≡ a2(mod m2)
∴ x=m1k1+a1,x=m2k2+a2
∴ m1k1+a1 = m2k2+a2
移项可得 m1k1-m2k2 = a2-a1
∵ m1,m2,a1,a2均可知
∴ 可套用扩展欧几里得公式计算k1’,k2’使其满足m1k1’+m2k2’ = d
k1=k1’(a2-a1)/d,k2=k2’(a2-a1)/d
∵ 需要求得最小正整数x,可利用公式先求通解K1
(ax+by=d x,y不唯一
x=x0+b/d * k,y=y0-a/d* k,k∈Z)
∵ K1 = k1 + k * m2/d
x=m1K1+a1=(k1+k * m2/d)m1+a1=m1k1+a1+m1 * k * m2/d
a=m1k1+a1
m=m1m2/d
x=ak+m
即求x ≡ a(mod m)
易知当x=a时,是满足条件的最小值,输出a即可

模板题

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    LL d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main(){
    int n;
    LL a1,a2,m1,m2,k1,k2;
    scanf("%d",&n);
    scanf("%lld %lld",&m1,&a1);
    for(int i=2;i<=n;i++){
        scanf("%lld %lld",&m2,&a2);
        LL d=exgcd(m1,m2,k1,k2);
        if((a2-a1)%d){
            a1=-1;
            break;
        }
        k1*=(a2-a1)/d;
        k1=(k1%(m2/d)+m2/d)%(m2/d); 
        a1=k1*m1+a1;
        m1=abs(m1/d*m2);
    }
    printf("%lld\n",a1);
    return 0}

Day4:

高斯消元

时间复杂度O(n³)

a11x1+a12x2+a13x3+…+a1nxn=b1
a21x1+a22x2+a23x3+…+a2nxn=b2

an1x1+an2x2+an3x3+…+annxn=bn
求x1,x2,x3…xn
(线性代数化为上三角型,利用行列式的性质)

做法:for(枚举每一列){
1.找到绝对值最大的一行(除了已经处理过的行)
2.将该行换到未处理的最上面
3.将该行的第一个数变成1
4.将下面所有本列的数变成0
}
5.利用主对角线的1,清空其余数,使其仅主对角线存在元素
6.xn=bn

变为上三角型后,若为完美阶梯型则有唯一解,若解得某式子出现0=非零数则无解,若解出0=0则有无穷多组解

今天还做了几道中国剩余定理的题,有一道比较神奇的还没有A,A了回来补充~
组合数也学了一点点,明天继续,学完了写在明天的博客里~

Day5

组合数

方法一:公式递推

时间复杂度O(N^2)

C m n = C m − 1 n − 1 ⋅ C m − 1 n C_m^n=C_{m-1}^{n-1}⋅C_{m-1}^n Cmn=Cm1n1Cm1n

公式证明:
若在m个物品中选出n个物品即 C m n C_m^n Cmn
现在从m个物品中选出一个物品,对于这个物品仅有两种可能
①选,那么还需要从余下的m-1个物品中选择n-1个,即 C m − 1 n − 1 C_{m-1}^{n-1} Cm1n1
②不选,那么还需要从余下的m-1个物品中选择n个,即 C m − 1 n C_{m-1}^n Cm1n
这两种情况已经包含了全部情况,所以 C m n = C m − 1 n − 1 ⋅ C m − 1 n C_m^n=C_{m-1}^{n-1}⋅C_{m-1}^n Cmn=Cm1n1Cm1n

模板题
(1≤n≤10000 1≤b≤a≤2000,查询次数多,数据范围小)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7,N=2010;
LL c[N][N];
void init(){
    for(int i=0;i<N;i++){
        for(int j=0;j<=i;j++){
            if(!j) c[i][j]=1;
            else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
}

int main(){
    init();
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        LL a,b;
        scanf("%lld %lld",&a,&b);
        printf("%lld\n",c[a][b]);
    }
    return 0;
}

方法二:预处理

C m n = m ! ( n − m ) ! n ! C_m^n=\frac{m!}{(n-m)!n!} Cmn=(nm)!n!m!

时间复杂度O(NlogN)

如果模数为质数,可以根据费马小定理利用快速幂求逆元

(1≤n≤10000 1≤b≤a≤1e5,数据范围较大)

模板题

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7,N=1e5+10;
LL q[N],inq[N];
int ksm(int a,int b,int p){
    LL ans=1,x=a;
    while(b){
        if(b&1) ans=ans*x%p;
        x=x*x%p;
        b>>=1;
    }
    return ans;
}
void init(){
    q[0]=inq[0]=1;
    for(int i=1;i<N;i++){
        q[i]=q[i-1]*i%mod;
        inq[i]=ksm(q[i],mod-2,mod);
    }
}
int main(){
    init();
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        int a,b;
        scanf("%d %d",&a,&b);
        LL ans=q[a]*inq[b]%mod*inq[a-b]%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

方法三:卢卡斯定理

时间复杂度O(plogN)

C m n = C m / p n / p ⋅ C m m o d p n m o d p ( m o d p ) C_m^n=C_{m/p}^{n/p}⋅C_{m mod p}^{n mod p}(mod p) Cmn=Cm/pn/pCmmodpnmodp(modp)

定理证明 这个公式背住就可以,目前没发现变形应用,不必掌握推导方法

模板题

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

LL ksm(LL a,LL b,LL p){
    LL ans=1,x=a;
    while(b){
        if(b&1) ans=ans*x%p;
        x=x*x%p;
        b>>=1;
    }
    return ans;
}

LL C(LL a,LL b,LL p){
    if(b>a) return 0;
    LL ans=1;
    for(int i=1,j=a;i<=b;i++,j--){
         ans=ans*j%p;
         ans=ans*ksm(i,p-2,p)%p;
    }
    return ans;
}

LL lucas(LL a,LL b,LL p){
    if(a<p&&b<p) return C(a,b,p);
    else return C(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
}
int main(){
    int n;
    LL a,b,p;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld %lld %lld",&a,&b,&p);
        printf("%lld\n",lucas(a,b,p));
    }
    return 0;
}

Day 6:

题目 高精度+组合数

C m n = m ! ( n − m ) ! n ! C_m^n=\frac{m!}{(n-m)!n!} Cmn=(nm)!n!m!

可以通过计算m,n-m,m的质因子个数来化简本题,对于质因子p的数量sl§即为,sl§=sl(a!)-sl((n-m)!)-sl(m!),再利用高精度乘法求解即可

#include<bits/stdc++.h>
using namespace std;
const int N=5000+10;
int prime[N],cnt,sum[N],st[N];

void sushu(int n){
    for(int i=2;i<=n;i++){
        if(!st[i]){
            prime[cnt]=i;
            cnt++;
        }  
        for(int j=0;prime[j]<=n/i;j++){
            st[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}

int Sum(int n,int p){
    int ans=0;
    while(n){
        ans+=n/p;
        n/=p;
    }
    return ans;
}

vector<int>mul(vector<int>&A,int b){
    vector<int>C;
    int t=0;
    for(int i=0;i<A.size()||t;i++){
        if(i<A.size()) t+=A[i]*b;
        C.push_back(t%10);
        t/=10;
    }
    while(C.size()>1&&C.back()==0) C.pop_back();
    return C;
}

int main(){
    int a,b;
    scanf("%d %d",&a,&b);
    sushu(a);
    for(int i=0;i<cnt;i++){
        int p=prime[i];
        sum[i]=Sum(a,p)-Sum(a-b,p)-Sum(b,p);
    }
    vector<int>res;
    res.push_back(1);
    for(int i=0;i<cnt;i++){
        for(int j=0;j<sum[i];j++){
            res=mul(res,prime[i]);
        }
    }
    for(int i=res.size()-1;i>=0;i--) printf("%d",res[i]);
    return 0;
}

Day 7:

卡特兰数

通过例题推理证明一下
给定n个0和n个1,它们将按照某种顺序排成长度为2n的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中0的个数都不少于1的个数的序列有多少个。

假设在一个n * n的坐标系里,从原点(0,0)出发到点(n,n)结束,序列里的0表示向右走一格,1表示向上走一格。
假设n=6,题目要求任意前缀序列中0的个数都需要大于等于1的个数,即所有合法路线必须严格处于红线下方,所以答案即为全部情况-经过红线的情况 C 12 6 − x C_{12}^6-x C126x
随意给出一条经过红线的路线,如图黄色线所示,将其与红线相交的第一个点到终点的路线,关于红线做对称线,如图粗绿线。可以发现绿线的终点为(5,7),易知所有经过红线到达(6,6)的路线经对称终点均为(5,7),易知从(0,0)出发到达(5,7)的所有路线必须穿过红线,即求从(0,0)到(5,7)的所有路线 x = C 12 5 x=C_{12}^5 x=C125
所以答案等于 C 12 6 − C 12 5 C_{12}^6-C_{12}^5 C126C125

卡特兰数 C 2 n n − C 2 n n − 1 = C 2 n n n + 1 C_{2n}^n-C_{2n}^{n-1}=\frac{C_{2n}^n}{n+1} C2nnC2nn1=n+1C2nn

题目链接

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
int ksm(int a,int b){
    int ans=1,x=a;
    while(b){
        if(b&1) ans=(LL)ans*x%mod;
        x=(LL)x*x%mod;
        b>>=1;
    }
    return ans;
}
int main(){
    int n,ans=1;
    scanf("%d",&n);
    for(int i=2*n;i>n;i--) ans=(LL)ans*i%mod;
    for(int i=1;i<=n;i++) ans=(LL)ans*ksm(i,mod-2)%mod;
    ans=(LL)ans*ksm(n+1,mod-2)%mod;
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值