zoukankan      html  css  js  c++  java
  • 数论总结(详细)


     

    线性筛:

    所有积性函数都能使用线性筛,线性筛保证每个数只会被它的最小质因子给筛掉,所以时间是线性的。

    线性筛质数:

    void init(int n)
    {
        for(ri i=2;i<=n;++i){
            if(!pri[i]) su[++cnt]=i;
            for(ri j=1;j<=cnt && su[j]*i<=n;++j){
                int k=su[j]*i;
                pri[k]=1;
                if(i%su[j]==0) break;
            }
        }
    }
    View Code

    线性筛约数个数与约数和公式详解

    线性筛约数个数:

    void init(int n)
    {
        for(ri i=2;i<=n;++i){
            if(!pri[i]) su[++cnt]=i,num[i]=2,tot[i]=1;//当i为质数的时候 正约数的个数为 1与i 
            for(ri j=1;j<=cnt && su[j]*i<=n;++j){
                int k=su[j]*i;
                pri[k]=1;
                if(i%su[j]==0){
                    num[k]=num[i]/(tot[i]+1)*(tot[i]+2);//质因子个数从原来的tot+1 变成tot+2 
                    tot[k]=tot[i]+1;//在i的基础上多了su[j]这个质数 
                    break;
                }//积性函数的性质 
                else num[k]=num[i]*num[su[j]],tot[k]=1;//i*su[j]包含了su[j]这个质因子 tot从1开始重新统计贡献 
            }
        }
    }
    View Code

    线性筛约数和:

    void init(int n)
    {
        for(ri i=2;i<=n;++i){
            if(!pri[i]) su[++cnt]=i,sum1[i]=1+i,sum2[i]=1+i;//质数的约数只有1和它本身 
            for(ri j=1;j<=cnt&&su[j]*i<=n;++j){
                int k=su[j]*i;
                pri[k]=1;
                if(i%su[j]==0){
                    sum1[k]=sum1[i]/sum2[i]*(sum2[i]*su[j]+1);//去掉原有的 乘上现在的 
                    sum2[k]=sum2[i]*su[j]+1;
                    break;
                }
                else{
                    sum1[k]=sum1[i]*(su[j]+1);//su[j]+1==sum1[su[j]] 
                    sum2[k]=1+su[j];//从下一个新的约数开始统计贡献 
                } 
            }
        }
    }
    View Code

    线性筛莫比乌斯函数:

    void init()
    {
        u[1]=1;
        for(int i=2;i<=maxn;i++){
            if(!pri[i]) su[++cnt]=i,u[i]=-1;
            for(int j=1;j<=cnt&&su[j]*i<=maxn;j++){
                if(i%su[j]==0){
                    pri[i*su[j]]=1; u[i*su[j]]=0;
                    break;
                }
                pri[i*su[j]]=1;
                u[i*su[j]]=-u[i];
            }
        }
    }
    View Code

    线性筛欧拉函数:

    void init(int n)
    {
        for(ri i=2;i<=n;++i){
            if(!pri[i]) su[++cnt]=i,ol[i]=i-1;
            for(ri j=1;j<=cnt&&su[j]*i<=n;++j){
                pri[su[j]*i]=1;
                if(i%su[j]==0){
                    ol[su[j]*i]=ol[i]*su[j];
                    break;
                }
                else ol[su[j]*i]=ol[i]*(su[j]-1);
            }
        }
    }
    View Code

    线性筛好题


     

    质数 && 约数:

    1.求 l~r 内素数的个数:洛谷P1835 素数密度_NOI导刊2011提高(04)

    数论里面,对于 l 和 r 很大,r-l 范围又很小的时候,都用到了用一个数组x[ i ]表示 i+l 的值,也就是把 l~r 这个区间对应到 0~l-1 中了。

    同时也预处理了可能对答案做出贡献的值(一般是预处理根号内的素数)。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000005
    #define ll long long
    #define ri register int
    int cnt=0,ans=0,pri[N],su[N],fl[N];
    ll l,r;
    void suu(int n)
    {
        for(ri i=2;i<=n;++i){
            if(!pri[i]) su[++cnt]=i;
            for(ri j=1;j<=cnt&&su[j]*i<=n;++j){
                pri[su[j]*i]=1;
                if(i%su[j]==0) break;
            }
        }
    }
    int main()
    {
        scanf("%lld%lld",&l,&r);
        suu(sqrt(r));
        for(int i=1;i<=cnt;++i){
            for(ll j=l/su[i]*su[i];j<=r;j+=su[i]){
                if(j>=l && j!=su[i]) fl[j-l]=1;
            }
        }
        for(ll i=l;i<=r;++i) if(fl[i-l]==0) ans++;
        printf("%d
    ",ans);
    }
    /*
    11
    */
    
    筛大素数
    View Code

    2.求A^B所有约数和(A,B<=5e7):洛谷P1593 因子和

    题解

    3.求1~n范围里约数的约数的个数加起来最多的是哪个数 及其总数

    题解4.给定n和m,求Σ(1<=i<=n)   Σ(1<=j<=m)   GCD(i,j)*2-1  :洛谷P1447 [NOI2010]能量采集

    /*
    来源:洛谷第一篇题解 
    题目大意:给定n和m,求Σ(1<=i<=n)Σ(1<=j<=m)GCD(i,j)*2-1
    
    i和j的限制不同,传统的线性筛法失效了,这里我们考虑容斥原理
    
    令f[x]为GCD(i,j)=x的数对(i,j)的个数,这个不是很好求
    
    我们令g[x]为存在公因数=x的数对(i,j)的个数(注意不是最大公因数!),显然有g[x]=(n/x)*(m/x)
    
    但是这些数对中有一些的最大公因数为2d,3d,4d,我们要把他们减掉
    
    于是最终f[x]=(n/x)*(m/x)-Σ(2*x<=i*x<=min(m,n))f[i*x]
    
    从后向前枚举x即可
    
    时间复杂度O(nlogn)
    */
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define N 200005
    ll n,m,f[N],ans=0;
    int main()
    {
        scanf("%lld%lld",&n,&m);
        ll minn=min(n,m);
        for(ll i=minn;i>=1;--i){
            //计算n,m中有多少对公约数为 i的(注意不是最大公约数) 
            f[i]=(n/i)*(m/i);
            //减去公约数为i*2 i*3 ...的 
            for(ll j=i*2;j<=minn;j+=i) f[i]-=f[j];
            //根据题意统计答案 
            ans+=f[i]*(2*i-1);
        }
        printf("%lld
    ",ans);
    }
    /*
    5 4
    */
    代码+题解

    BSGS:

    BSGS模板(互质与不互质) 洛谷P4195、P3846

    没有做到不是模板的BSGS


    中国剩余定理:

    1.模板:洛谷P3868 [TJOI2009]猜数字

    注意有坑

    2.扩展+模板+详解

    3.综合运用:P2480 [SDOI2010]古代猪文

    #include<bits/stdc++.h>
    using namespace std;
    #define Mod 999911659
    #define ll long long
    #define ri register int 
    #define N 40005
    ll p[5]={0,2,3,4679,35617},fac[N],xx[5];
    ll quick_pow(ll a,ll k,ll mod)
    {
        ll ans=1;
        while(k){ if(k&1) ans=ans*a%mod; a=a*a%mod; k>>=1; }
        return ans;
    }
    void init(ll n)
    {
        fac[0]=1; for(ll i=1;i<=n;++i) fac[i]=fac[i-1]*i %n;
    }
    ll C(ll n,ll m,ll mod)
    {
        if(n<m) return 0;
        return fac[n]* quick_pow(fac[n-m],mod-2,mod) %mod *quick_pow(fac[m],mod-2,mod) %mod;
    }
    ll lucas(ll n,ll m,ll mod)
    {
        if(n<m) return 0;
        if(m==0 || n==0) return 1; 
        return lucas(n/mod,m/mod,mod) * C(n%mod,m%mod,mod) %mod;
    }
    void work1(ll n)
    {
        for(ri i=1;i<=4;++i){
            init(p[i]);
            for(ri j=1;j*j<=n;++j)
            if(n%j==0){
                xx[i]=( xx[i] + lucas(n,j,p[i]) ) %p[i];
                if(j*j!=n) xx[i]=( xx[i] + lucas(n,n/j,p[i]) ) %p[i];
            }
        }
    }
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b) { x=1; y=0; return a; }
        ll d=exgcd(b,a%b,x,y);
        ll z=x; x=y; y=z-(a/b)*y;
        return d;
    }
    /*ll work2()中国剩余定理用扩欧求会TLE。。。 
    {
        ll M=Mod-1,ans=0;
        for(ri i=1;i<=4;++i){
            ll mi=M/p[i],a=mi,b=p[i];
            ll x,y,d=exgcd(a,b,x,y);
            ans=( ans + x*mi %M *xx[i] %M ) %M;
        }
        return ans;
    }*/
    ll work2()
    {
        ll ans=0,M=Mod-1;//直接用逆元解同余方程 
        for(ri i=1;i<=4;++i) ans=( ans+ quick_pow(M/p[i],p[i]-2,p[i]) %M *xx[i] %M *(M/p[i]) %M) %M;
        return ans;
    }
    int main()
    {
        ll q,n;
        scanf("%lld%lld",&n,&q);
        if(q==Mod) {
            printf("0
    "); return 0;
        }
        work1(n);
        printf("%lld
    ",quick_pow(q,work2(),Mod));
    }
    /*
    4 2
    
    237573745 375736583
    */
    View Code

    高斯消元:

    详解


    矩阵乘法:

    详解


    欧拉函数 && 欧拉定理:

    详解


    exgcd:

    1.P1516 青蛙的约会 :列式子求exgcd即可(注意判负数)

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b) { x=1; y=0; return a; }
        ll d=exgcd(b,a%b,x,y);
        ll z=x; x=y; y=z-(a/b)*y;
        return d;
    }
    int main()
    {
        ll xx,yy,m,n,L;
        scanf("%lld%lld%lld%lld%lld",&xx,&yy,&m,&n,&L);
        ll a=m-n,b=L,c=yy-xx;
        //if(a<0) a=-a,c=-c;
        ll x,y,d=exgcd(a,b,x,y);
        //printf("%lld %lld %lld %lld =%lld
    ",a,x,b,y,d);
        if(c%d) printf("Impossible
    ");
        else{
            x=( (c/d)*x %(abs(b/d)) +abs(b/d) ) %abs(b/d);//mod一定是正数!! 否则mod出来也会是负数 
            printf("%lld
    ",x);
        }
        return 0;
    }
    View Code

    2.[NOI2002]荒岛野人枚举模数,用exgcd检查即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100005
    int n,c[N],p[N],l[N];
    int exgcd(int a,int b,int &x,int &y)
    {
        if(b==0) { x=1; y=0; return a; }
        int d=exgcd(b,a%b,y,x);
        y=y-(a/b)*x;
        return d;
    }
    bool check(int m)
    {
        for(int i=1;i<=n;i++)
         for(int j=i+1;j<=n;j++)
         {
             int a=p[i]-p[j],b=m,cc=c[j]-c[i],x,y;
             int d=exgcd(a,b,x,y);
            if(cc%d) continue;//无解成立
            //x是相遇的最小整数天数 如果大于寿命则成立 
            //c%d==0 所以对应的x应该为x*(c/d);
            b=abs(b/d);
            x=(x*(cc/d)%b+b)%b;
            if(x<=l[i]&&x<=l[j]) return 0;//会相遇就直接判m不成立 
         }
         return 1; 
    }
    int main()
    {
        int mx=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) 
         scanf("%d%d%d",&c[i],&p[i],&l[i]),mx=max(c[i],mx);
        for(int i=mx;;i++)
        if(check(i)) return printf("%d
    ",i),0;
    }
    /*
    3
    1 3 4
    2 7 3
    3 2 1
    ans 6
    */
    View Code

    3.求解方程的正整数解个数,及和:

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll gcd(ll a,ll b) { return b ? gcd(b,a%b) : a; }
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b) { x=1; y=0; return a; }
        ll d=exgcd(b,a%b,x,y);
        ll z=x; x=y; y=z-(a/b)*y;
        return d;
    }
    int main()
    {
        freopen("pay.in","r",stdin);
        freopen("pay.out","w",stdout);
        int T,opt;
        scanf("%d%d",&T,&opt);
        while(T--){
            ll a,b,c;
            scanf("%lld%lld%lld",&a,&b,&c);
            ll x,y,d=exgcd(a,b,x,y);
            if(c%d){
                if(opt==1) printf("0
    ");
                else printf("0 0
    ");
                continue;
            }
            x=( (c/d)*x %(b/d) + b/d ) % (b/d);//把x变成最小非负整数解  b/d是x的增量  
            y=( c-a*x )/b;
            if(y<0){//如果说x的最小非负整数解y都小于0了 说明只有一个为负 一个为正的解 不符合题意 
                if(opt==1) printf("0
    ");
                else printf("0 0
    ");
                continue;
            }
            ll ans1=0,ans2=0;
            /*while(y>=0){//暴力跳解 其实只需要求一下等差序列的和即可 
                if(x>=0 && y>=0) ans1++,ans2+=x+y;
                x+=b/d; y-=a/d;
            }*/
            int x1=y/(a/d),x2=y%(a/d);//推公式求等差序列 
            ans2+=(a/d)*(x1+1)*x1/2 + (x1+1)*x2;
            ans2+=(b/d)*(x1+1)*x1/2 + (x1+1)*x;
            ans1=x1+1;//推式子 
            if(opt==1) printf("%lld
    ",ans1);
            else printf("%lld %lld
    ",ans1,ans2);
        }
    }
    /*
    2 2
    
    6 9 3
    
    3 4 21
    2 4 12
    */
    View Code

    注意x的增量是:b/d,通过求特解来求出所有满足情况的解。


    逆元:

    求逆元的几种常用方法


    排列组合:

    1. 组合数的常用公式大全

    2.P4071 [SDOI2016]排列计数 :错排递推+组合数

    //luogu4071 
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define ri register int
    #define N 1000005
    const ll mod=1e9+7;
    ll f[N],invfac[N],fac[N];
    ll C(int n,int m)
    {
        return fac[n]*invfac[n-m] %mod *invfac[m] %mod;
    }
    ll quick_pow(ll a,ll k)
    {
        ll ans=1;
        while(k){
            if(k&1) ans=ans*a%mod; a=a*a%mod; k>>=1;
        }
        return ans;
    }
    void init()
    {
        int nn=1e6;
        f[0]=1; f[1]=0; f[2]=1;
        for(ri i=3;i<=nn;++i) f[i]=(i-1)*( ( f[i-1]+f[i-2]) %mod ) %mod;
        fac[0]=1;
        for(ri i=1;i<=nn;++i) fac[i]=fac[i-1]*i %mod;
        invfac[nn]=quick_pow(fac[nn],mod-2);
        for(ri i=nn;i>=1;--i) invfac[i-1]=invfac[i]*i %mod;
    }
    int main()
    {
        int T,n,m;
        scanf("%d",&T);
        init();
        while(T--){
            scanf("%d%d",&n,&m);
            if(n<m) printf("0
    ");
            else printf("%lld
    ",f[n-m]*C(n,m)%mod);
        }
    }
    View Code

    3.problem 题(Catlan + dp + 组合数)

    4.组合数插板法学习:(转载自here

    /*排列组合插板法小结
    
    插板法就是在n个元素间的(n-1)个空中插入若干个(b)个板,可以把n个元素分成(b+1)组的方法。
    
    应用插板法必须满足三个条件:
    
    (1)这n个元素必须互不相异
    
    (2)所分成的每一组至少分得一个元素
    
     (3) 分成的组别彼此相异
    
    举个很普通的例子来说明
    
    把10个相同的小球放入3个不同的箱子,每个箱子至少一个,问有几种情况?
    
    问题的题干满足条件(1)(2),适用插板法,c9 2=36
    
    下面通过几道题目介绍下插板法的应用
    
    ===================================================
    
    a 凑元素插板法(有些题目满足条件(1),不满足条件(2),此时可适用此方法)
    
    例1 :把10个相同的小球放入3个不同的箱子,问有几种情况?
    
    3个箱子都可能取到空球,条件(2)不满足,此时如果在3个箱子种各预先放入
    
    1个小球,则问题就等价于把13个相同小球放入3个不同箱子,每个箱子至少一个,有几种情况?
    
    显然就是c12 2=66
    
    -------------------------------------------------
    
    例2:把10个相同小球放入3个不同箱子,第一个箱子至少1个,第二个箱子至少3个,第三个箱子可以放空球,有几种情况?
    
    我们可以在第二个箱子先放入10个小球中的2个,小球剩8个放3个箱子,然后在第三个箱子放入8个小球之外的1个小球,则问题转化为把9个相同小球放3不同箱子,每箱至少1个,几种方法?c8 2=28
    
    ==================================================
    
    b 添板插板法
    
    例3:把10个相同小球放入3个不同的箱子,问有几种情况?
    
    -o - o - o - o - o - o - o - o - o - o - o表示10个小球,-表示空位
    
    11个空位中取2个加入2块板,第一组和第三组可以取到空的情况,第2组始终不能取空
    
    此时若在第11个空位后加入第12块板,设取到该板时,第二组取球为空
    
    则每一组都可能取球为空c12 2=66
    
    --------------------------------------------------------
    
    例4:有一类自然数,从第三个数字开始,每个数字都恰好是它前面两个数字之和,直至不能再写为止,如257,1459等等,这类数共有几个?
    
    因为前2位数字唯一对应了符合要求的一个数,只要求出前2位有几种情况即可,设前两位为ab
    
    显然a+b<=9 ,且a不为0
    
    1 -1- 1 -1 -1 -1 -1 -1 -1 - - 1代表9个1,-代表10个空位
    插一个板使得b可以为0 
    我们可以在这9个空位中插入2个板,分成3组,第一组取到a个1,第二组取到b个1,但此时第二组始终不能取空,若多添加第10个空时,设取到该板时第二组取空,即b=0,所以一共有c10 2=45
    
    -----------------------------------------------------------
    
    例5:有一类自然数,从第四个数字开始,每个数字都恰好是它前面三个数字之和,直至不能再写为止,如2349,1427等等,这类数共有几个?
    
    类似的,某数的前三位为abc,a+b+c<=9,a,b不为0
    
    1 -1- 1 -1 -1 -1 -1 -1 -1 - - -
    这道题只需要保证a不为0 但是插3个板会使b,c不为0 而这道题b,c可以为0 所以插两个板表示它们取空的情况  
    在9个空位中插入3板,分成4组,第一组取a个1,第二组取b个1,第三组取c个1,由于第二,第三组都不能取到空,所以添加2块板
    
    设取到第10个板时,第二组取空,即b=0;取到第11个板时,第三组取空,即c=0。所以一共有c11 3=165
    
    ============================================
    
    c 选板法
    
    例6:有10粒糖,如果每天至少吃一粒(多不限),吃完为止,求有多少种不同吃法?
    
    o - o - o - o - o - o - o - o - o - o o代表10个糖,-代表9块板
    
    10块糖,9个空,插入9块板,每个板都可以选择放或是不放,相邻两个板间的糖一天吃掉
    
    这样一共就是2^9= 512啦
    
    =============================================
    
    d 分类插板
    
    例7:小梅有15块糖,如果每天至少吃3块,吃完为止,那么共有多少种不同的吃法?
    
    此问题不能用插板法的原因在于没有规定一定要吃几天,因此我们需要对吃的天数进行分类讨论
    
    最多吃5天,最少吃1天
    
    1:吃1天或是5天,各一种吃法一共2种情况
    
    2:吃2天,每天预先吃2块,即问11块糖,每天至少吃1块,吃2天,几种情况?c10 1=10
    
    3:吃3天,每天预先吃2块,即问9块糖,每天至少1块,吃3天? c8 2=28
    
    4:吃4天,每天预先吃2块,即问7块糖,每天至少1块,吃4天?c6 3=20
    
    所以一共是2+10+28+20=60 种
    
    =================================
    
    e 二次插板法
    
    例8 :在一张节目单中原有6个节目,若保持这些节目相对次序不变,再添加3个节目,共有几种情况?
    
    -o - o - o - o - o - o - 三个节目abc
    三个一起不好处理 可以分成一个一个地处理 
    可以用一个节目去插7个空位,再用第二个节目去插8个空位,用最后个节目去插9个空位
    
    所以一共是c7 1×c8 1×c9 1=504种
    
    -----------------------------------------------------------*/
    View Code

    待更新。。。

  • 相关阅读:
    【BZOJ4383】[POI2015]Pustynia 线段树优化建图
    【BZOJ4519】[Cqoi2016]不同的最小割 最小割树
    【BZOJ2229】[Zjoi2011]最小割 最小割树
    【BZOJ2151】种树 双向链表+堆(模拟费用流)
    Python入门之Pycharm开发中最常用快捷键
    Python Web学习笔记之GIL机制下的鸡肋多线程
    SQL学习之Can't connect to MySQL server on localhost (10061)
    win10锁屏界面无法更新
    如何安装Pycharm官方统计代码行插件
    Notepad++ 主题配色配置
  • 原文地址:https://www.cnblogs.com/mowanying/p/11687915.html
Copyright © 2011-2022 走看看