逆元:对于正整数a,如果有ax≡1 (mod m),那么把这个同余方程中的最小正整数解x叫做a模m的逆元。
对于缩系中的元素,每个数a均有唯一的与之对应的乘法逆元x,使得ax≡1(mod n)
一个数有逆元的充分必要条件是gcd(a,n)=1,此时逆元唯一存在
逆元的含义:模n意义下,1个数a如果有逆元x,那么除以a相当于乘以x。
这里介绍三种方法求逆元:
1、费马小定理求逆元。
2、拓展gcd求逆元。
3、欧拉定理。
费马小定理求逆元
在模为素数p的情况下,有费马小定理 a^(p-1)=1(mod p) 那么a^(p-2)=a^-1(mod p) 也就是说a的逆元为a^(p- 2)而在模不为素数p的情况下,有欧拉定理 a^phi(m)=1(mod m) (a⊥m) 同理a^-1=a^(phi(m)-1)因此逆元x便可 以套用快速幂求得了x=a^(phi(m)-1)但是似乎还有个问题?如何判断a是否有逆元呢? 检验逆元的性质,看求出值 x与a相乘是否为1即可
PS:这种算法复杂度为O(log2N)在几次测试中,常数似乎较上种方法大
当p比较大的时候需要用快速幂求解
代码如下:
typedef long long ll;
ll pow_mod(ll x, ll n, ll mod){
ll res=1;
while(n>0){
if(n&1)res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
拓展gcd求逆元
给定模数m,求a的逆相当于求解ax=1(mod m)这个方程可以转化为ax-my=1 然后套用求二元一次方程的方法,用拓展欧几里得算法求得一组x0,y0和gcd 检查gcd是否为1 ,gcd不为1则说明逆元不存在 ,若为1,则调整x0到0~m-1的范围中即可
PS:这种算法效率较高,常数较小,时间复杂度为O(ln n)
代码如下:
typedef long long ll;
void extgcd(ll a,ll b,ll& d,ll& x,ll& y){
if(!b){ d=a; x=1; y=0;}
else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); }
}
ll inverse(ll a,ll n){
ll d,x,y;
extgcd(a,n,d,x,y);
return d==1?(x+n)%n:-1;
}
欧拉定理求逆元
当模p不是素数的时候需要用到欧拉定理a^phi(p)≡1(mod p),a*a^(phi(p)-1)≡1(mod p)
由于 ax≡1 (mod m) ,
故 x =1/a(mod m)
故 (b/a)modm
===>(b*x)modm
===>(b%m* x%m)%m
我们来看几道例题:
A/B
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 7160 Accepted Submission(s): 5700
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。
2 1000 53 87 123456789
7922 6060
ans= (A/B)%9973
=(A*b)%9973 b 为B的逆元
=(A%9973 * b%9973) %9973
=n*b%9973
故只需求B的逆元即可得到答案。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mod(x,y) (x%y+y)%y
#define ll long long int
using namespace std;
long long exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll re=exgcd(b,a%b,y,x);
y=y-x*(a/b);
return re;
}
long long ine(ll a,ll n)
{
ll x,y;
ll gcd=exgcd(a,n,x,y);
return mod(x,n);
}
int main()
{
int t;
cin>>t;
while(t--)
{
ll n,b;
cin>>n>>b;
ll m=n*ine(b,9973)%9973;
cout<<m<<endl;
}
return 0;
}
二, hdu-5685
Problem A
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1635 Accepted Submission(s): 737
H(s)=∏
S
请帮助度熊计算大字符串中任意一段的哈希值是多少。
1≤N≤1,000
1≤len(string)≤100,000
1≤a,b≤len(string)
2 ACMlove2015 1 11 8 10 1 testMessage 1 1
6891 9240 88
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
typedef long long ll;
#define mod(x,y) (x%y+y)%y
#define N 100005
using namespace std;
char str[N];
int sum[N];
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return ;
}
exgcd(b,a%b,y,x);
y=y-x*(a/b);
}
long long ine(ll a,ll n)
{
ll x,y;
exgcd(a,n,x,y);
return mod(x,n);
}
int main()
{
int t;
while(cin>>t)
{
scanf("%s",str+1);
// int len=strlen(str);
sum[0]=1;
for(int i=1;str[i]!=0;i++)
{
sum[i]=(sum[i-1]*(str[i]-28))%9973;
}
for(int i=0;i<t;i++)
{
ll a,b;
cin>>a>>b;
ll tmp=ine(sum[a-1],9973);
ll m=sum[b]*tmp%9973;
cout<<m<<endl;
}
}
return 0;
}