zoukankan      html  css  js  c++  java
  • 逆元

    对于正整数,如果有,那么把这个同余方程中的最小正整数解叫做的逆元。

    逆元一般用扩展欧几里得算法来求得,如果为素数,那么还可以根据费马小定理得到逆元为

    推导过程如下

                                

    还有一个公式:a/b mod m=a mod bm/b;

    3.线性求逆元

    其实有些题需要用到的所有逆元,这里为奇质数。那么如果用快速幂求时间复杂度为

    如果对于一个1000000级别的素数,这样做的时间复杂度是很高了。实际上有的算法,有一个递推式如下

                       

    它的推导过程如下,设,那么

           

    对上式两边同时除,进一步得到

           

    再把替换掉,最终得到

           

    初始化,这样就可以通过递推法求出模奇素数的所有逆元了。

     

    1.hdu1576 http://acm.hdu.edu.cn/showproblem.php?pid=1576

    A/B

    Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 5597    Accepted Submission(s): 4371


    Problem Description
    要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。
     
    Input
    数据的第一行是一个T,表示有T组数据。
    每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。
     
    Output
    对应每组数据输出(A/B)%9973。
     
    Sample Input
    2 1000 53 87 123456789
     
    Sample Output
    7922 6060
     
    /*
    扩展欧几里得求逆元
    满足a*x 9973 b==1 9973 b 则x为a的逆元
    
    A/B=A*(B的逆元) 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define LL long long
    
    using namespace std;
    LL A,B,a,b,n,d,x,y;
    
    LL exgcd(int a,int b)
    {
        if(a==0&&b==0) return -1;
        if(b==0)
        {
            x=1;y=0;d=a;
            return d;
        }
        d=exgcd(b,a%b);
        LL tmp=x;x=y;y=tmp-a/b*y;
        return d;
    }
    
    LL inv(LL a,LL b)
    {
        d=exgcd(a,b);
        if(d==1)//a,b 互素 
          return (x%9973+9973)%9973;
        else return -1;
    }
    
    int main()
    {
        int T;cin>>T;
        while(T--)
        {
            scanf("%lld%lld",&n,&B);
            LL p=inv(B,9973);
            printf("%d
    ",n*p%9973);
        }
        return 0;
    }

    1.poj 1845 http://poj.org/problem?id=1845

    Sumdiv
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 21208   Accepted: 5339

    Description

    Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).

    Input

    The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.

    Output

    The only line of the output will contain S modulo 9901.

    Sample Input

    2 3

    Sample Output

    15

    Hint

    2^3 = 8. 
    The natural divisors of 8 are: 1,2,4,8. Their sum is 15. 
     
    /*
    题意:求A^B 所有约数(因子)之和mod 9901。
    
    应用质因数分解+约数和公式+逆元+等比数列求和公式
    A=p1^k1*p2^k2*...pn^kn
    A^B=p1^(k1*B)*p2^(k2*B)...pn^(kn*B)
    约数和公式:Sum=(1+p1+p1^2+...+p1^k1)*(1+p2+p2^2+...+p2^k2)*(......pk^kn)
    Sum(A^B)=(1+p1+p1^2+...+p1^k1*B)*(1+p2+p2^2+...+p2^k2*B)*(......pk^kn*B) mod 9901
    对于每一个 (1+p1+p1^2+...+p1^k1*B),根据等比数列求和公式为 [1-p1^(1+k1*B)]/(1-p1) mod 9901
    根据逆元公式:a/b mod m=(a mod mb )/b 
    原式= [p1^(k1*B+1)-1] mod [9901*(p1-1)] / (p1-1)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 10101
    #define mod 9901
    #define LL long long
    
    using namespace std;
    long long A,B;
    int cnt;
    int c[maxn],f[maxn],p[maxn],factor[maxn];
    
    void Eprime()//素数表 
    {
        f[0]=f[1]=1;
        for(int i=1;i<=maxn;i++) if(f[i]==0)
          for(int j=i+i;j<=maxn;j+=i)
            f[j]=1;
        for(int i=1;i<=maxn;i++)
          if(f[i]==0) p[cnt++]=i;
    }
    
    /*void Devide(int x) 
    {
        for(int i=1;i<=maxn;i++)
        {
            int P=p[i];if(x==1)break;
            while(x%P==0)
            {
                x/=P;
                c[++cnt]=P;
            }
        }
        for(int i=1;i<=cnt;i++) factor[c[i]]=1;
        for(int i=1;i<=cnt;i++)
          while(c[i]==c[i+1])
            factor[c[i]]++;
        sort(factor+1,factor+cnt);
        sort(c,c+cnt);
        num=unique(factor,factor+cnt)-factor;
        num=unique(c+1,c+cnt)-c;
        num--;
        for(int i=1;i<=num;i++)
          factor[i]*=B;
    }
    */
    
    LL Mul(LL a,LL b,LL m)//慢速乘,防止乘爆了 
    {  
        LL res=0;a%=m;
        while(b) 
        {  
            if(b & 1) res=(res+a)%m;
            b>>=1;  
            a=(a+a)%m;
        }  
        return res;  
    } 
    
    LL fast(LL a,LL b,LL m)//快速幂 
    {
        LL res=1;a%=m;
        while(b)
        {
            if(b&1) res=Mul(res,a,m);
            b>>=1;
            a=Mul(a,a,m);
        }
        return res;
    }
    
    void Devide(LL A,LL B)
    {
        LL ans=1;  
        for(int i=0;p[i]*p[i]<=A;i++)
        {
            int P=p[i];
            if(A%p[i]==0)
            {
                int num=0;
                while(A%P==0)
                {
                    num++;
                    A/=P;
                }
                //原式= [p1^(k1*B+1)-1] mod [9901*(p1-1)]/(p1-1)
                LL M=(P-1)*mod;  
                ans*=(fast(P,num*B+1,M)+M-1)/(P-1);
                ans%=mod;                     
            }
        } 
        if(A>1)//A是素数 
        {
            LL M=(A-1)*mod;
            ans*=(fast(A,B+1,M)+M-1)/(A-1);
            ans%=mod; 
        }
        printf("%lld
    ",ans);
    }
    
    int main()
    {
        Eprime();
        scanf("%lld%lld",&A,&B);
        Devide(A,B);
        return 0;
    }

    3.bzoj2486 http://www.lydsy.com/JudgeOnline/problem.php?id=2186
     

    2186: [Sdoi2008]沙拉公主的困惑

    Time Limit: 10 Sec  Memory Limit: 259 MB
    Submit: 3994  Solved: 1386
    [Submit][Status][Discuss]

    Description

      大富翁国因为通货膨胀,以及假钞泛滥,政府决定推出一项新的政策:现有钞票编号范围为1到N的阶乘,但是,政府只发行编号与M!互质的钞票。房地产第一大户沙拉公主决定预测一下大富翁国现在所有真钞票的数量。现在,请你帮助沙拉公主解决这个问题,由于可能张数非常大,你只需计算出对R取模后的答案即可。R是一个质数。

    Input

    第一行为两个整数T,R。R<=10^9+10,T<=10000,表示该组中测试数据数目,R为模后面T行,每行一对整数N,M,见题目描述 m<=n

    Output

    共T行,对于每一对N,M,输出1至N!中与M!素质的数的数量对R取模后的值

    Sample Input

    1 11
    4 2

    Sample Output

    1

    数据范围:
    对于100%的数据,1 < = N , M < = 10000000
    /*
    题目大意:给定询问组数T和取模数P,每次询问给定两个整数n和m,求1~(n!)的数中与m!互质的数个个数模P (m<=n)
    首先暴力肯定过不去,我们需要预处理一些东西
    首先我们知道,若x与y互质,则x+y与y也互质,x+2y与y也互质。。。
    换到这道题上来说,若一个数x与m!互质,那么x+(m!)也一定与m!互质,(x+m!*2)也一定与m!互质。。。
    由于n!一定是m!的倍数,于是我们每存在到一个x<=m!与m!互质,我们就一定能找到(n!)/(m!)个与m!互质的数
    而m!以内与m!互质的数的数量恰好是φ(m!)
    于是我们要得到的数恰好就是φ(m!)*(n!)/(m!) %p
    其中m!的所有质因数恰好就是m以内所有的质数 于是φ(m!)=(m!)*∏(pi-1)/pi (pi<=m)
    于是最后我们的结果就是n!*∏(pi-1)/pi
    质数预处理出来,n!预处理出来,pi的逆元预处理出来,∏(pi-1)/pi就可以预处理出来,都是线性的
    
    bzoj 上用读入优化卡过了
    洛谷比较强。。。 
    */
    #include<bitset>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define M 10000001
    #define ll long long
    
    using namespace std;
    bool not_prime[M+100];
    ll prime[500500],ans[M+100],fac[M+100],rev[M+100];
    int n,m,p,T,tot;
    
    void Linear_Shaker()
    {
        ll i,j;
        for(i=2;i<=M;i++)
        {
            if(!not_prime[i])
                prime[++tot]=i;
            for(j=1;j<=tot&&prime[j]*i<=M;j++)
            {
                not_prime[prime[j]*i]=1;
                if(i%prime[j]==0)
                    break;
            }
        }
        fac[1]=1;
        for(i=2;i<=M;i++)
            fac[i]=fac[i-1]*i%p;
        rev[1]=1;
        for(i=2;i<=M&&i<p;i++)
            rev[i]=(p-p/i)*rev[p%i]%p;
        ans[1]=1;
        for(i=2;i<=M;i++)
        {
            if(!not_prime[i])
                ans[i]=ans[i-1]*(i-1)%p*rev[i%p]%p;
            else
                ans[i]=ans[i-1];
        }
    }
    
    inline int init()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int main()
    {
        T=init();p=init();
        Linear_Shaker();
        for(int i=1;i<=T;++i)
        {
            n=init();m=init();
            printf("%d
    ",fac[n]*ans[m]%p);
        }
    }

    瞬间移动

    Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
    Total Submission(s): 1422    Accepted Submission(s): 684


    Problem Description
    有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n 行第m 列的格子有几种方案,答案对1000000007 取模。

     
    Input
    多组测试数据。

    两个整数n,m(2n,m100000)
     
    Output
    一个整数表示答案
     
    Sample Input
    4 5
     
    Sample Output
    10
     
    Source
     
     
    x和y分开考虑,在(1,1)到(n,m)之间可以选择走i步。就需要选i步对应的行C(n-2,i)及i步对应的列C(m-2,i)。相乘起来。 假设m<=n
     
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    #define N 200001
    #define M 1000000007
    #define ll long long
    
    using namespace std;
    ll fac[N]={1,1},inv[N]={1,1},f[N]={1,1};
    int n,m;
    
    ll C(ll a,ll b)
    {
        return fac[a]*inv[b]%M*inv[a-b]%M;
    }
    
    int main()
    {
        for(int i=2;i<N;i++)
        {
            fac[i]=fac[i-1]*i%M;
            f[i]=(M-M/i)*f[M%i]%M;
            inv[i]=inv[i-1]*f[i]%M;
        }
        while(~scanf("%d%d",&n,&m)) printf("%lld
    ",C(m+n-4,m-2));
        return 0;
    }
    折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
  • 相关阅读:
    make -j 8参数的作用
    使用请求头认证来测试需要授权的 API 接口
    查看Linux系统的平均负载
    服务器负载均衡的基本功能和实现原理
    Oracle RAC学习笔记:基本概念及入门
    详解物化视图(汇总比较有用的资料)
    程序优化注意的一些点
    PR 审批界面增加显示项方法
    Most Common Solutions to FRM-41839 and .tmp Files Not Being Deleted
    APPCORE Routine APIs
  • 原文地址:https://www.cnblogs.com/L-Memory/p/6751759.html
Copyright © 2011-2022 走看看