逆元

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

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

推导过程如下

                            

还有一个公式: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",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\n",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\n",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\n",C(m+n-4,m-2));
    return 0;
}

 

转载于:https://www.cnblogs.com/L-Memory/p/6751759.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值