大数取模
ll ans=0; for(int i=0; i<strlen(s); i++) { ans=(ans*10+s[i]-'0')%mod; }
快速幂
- 计算一个数的次幂即
快速幂的思想就是将指数转化成二进制再进行求解
其中指数,所以我们可以求出按照循环要循环次,现在只要计算次,所以快速幂的时间复杂度是long long power(int a,int n) { long long ans=1; long long base=a; while(n) { if(n&1) { ans=ans*base; } base=base*base; n>>=1; } return ans; }
欧几里得
- 求最大公约数
long long gcd(long long a,long long b) { if(b==0) { return a; } else { return gcd(b,a%b); } }
- 最小公倍数
扩展欧几里得
- 对于不完全为 的非负整数 表示 的最大公约数,必然存在整数对 , 使得 .
证明
- ①: 立即推
②:
设
设
已知: (欧几里得)
立即推
可得: ;
define ll long long
void exgcd(ll a,ll b,ll &gcd,ll &x,ll &y) { if(b==0) { x=1; y=0; gcd=a; } else { exgcd(b,a%b,gcd,y,x); y-=x*(a/b); } }
ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return a; } ll r = exgcd(b,a%b,y,x); y-=x*(a/b); return r; }
逆元
对于正整数,如果有
(即),那么把这个同余方程中的最小正整数解叫做模的逆元。逆元用途
如何求解,取模运算中
在这种情况下就要求在模的情况下的逆元了
即,
然后逆元求法
①.费马小定理
假如是一个质数
且那么
因为根据费马小定理可知
所以
②.扩展欧几里得算法
扩展欧几里得(互质 ,且不是质数时也可使用)
求解
解出的最小正整数解叫做模的逆元。ll inv ( ll a , ll b ) { ll gcd, x, y; exgcd(a, b, gcd, x, y); return gcd == 1 ? ( x % b + b ) % b : -1 ; // x 可能为负数 }
中国剩余定理
其中两两互质的整数结论:
其中
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int mm=1e6+10; ll c[mm],mod[mm]; ll exgcd(ll a,ll b,ll &gcd,ll &x,ll &y) { if(b==0) { x=1; y=0; gcd=a; } else { exgcd(b,a%b,gcd,y,x); y-=x*(a/b); } } ll inv(ll a,ll b) { ll gcd,x,y; exgcd(a,b,gcd,x,y); return gcd==1?(x%b+b)%b:-1; } int main( ) { int n; while(~scanf("%d",&n)) { ll ans=1; for(int i=1; i<=n; i++) { scanf("%lld%lld",&mod[i],&c[i]); ans*=mod[i]; } ll sum=0; for(int i=1; i<=n; i++) { ll a=ans/mod[i]; ll b=mod[i]; sum=(sum+c[i]*a*inv(a,b)%ans)%ans; } printf("%lld\n",sum); } return 0; }
扩展中国剩余定理
其中两两不一定互质的整数
令
根据扩展欧几里得算法求解,满足
则
实际上有多组解
取的最小整数解,
最后合成void exgcd(ll a,ll b,ll &gcd,ll &x,ll &y) { if(b==0) { x=1; y=0; gcd=a; } else { exgcd(b,a%b,gcd,y,x); y-=x*(a/b); } } ll mul(ll a,ll n,ll m)//快速乘 { ll ans=0; ll base=a; while(n) { if(n&1) { ans=(ans+base)%m; } base=(base+base)%m; n>>=1; } return ans%m; } ll excrt(int n) { ll x,y,gcd; ll M=1,ans=0;//k数组是被模数,mod数组是模数,k%mod for(int i=1;i<=n;i++) { ll a=M; ll b=mod[i]; ll c=(k[i]-ans%b+b)%b; exgcd(a,b,gcd,x,y); if(c%gcd!=0) { return -1; } ll t=b/gcd; x=mul(x,c/gcd,t); ans+=x*M;//更新前i个方程组的答案 M*=t;//前i个modi的小公倍数 ans=(ans%M+M)%M; } return (ans%M+M)%M; }
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int MAX=1e5+10;
ll k[MAX],mod[MAX];//mod数组是模数,k数组是被模数-->>k%mod
void exgcd(ll a,ll b,ll &gcd,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
gcd=a;
}
else
{
exgcd(b,a%b,gcd,y,x);
y-=x*(a/b);
}
}
ll mul(ll a,ll n,ll m)
{
ll ans=0;
ll base=a;
while(n)
{
if(n&1)
{
ans=(ans+base)%m;
}
base=(base+base)%m;
n>>=1;
}
return ans%m;
}
ll excrt(int n)
{
ll x,y,gcd;
ll M=1,ans=0;
for(int i=1; i<=n; i++)
{
ll a=M;
ll b=mod[i];
ll c=(k[i]-ans%b+b)%b;
exgcd(a,b,gcd,x,y);
if(c%gcd!=0)
{
return -1;
}
ll t=b/gcd;
x=mul(x,c/gcd,t);
ans+=x*M;
M*=t;
ans=(ans%M+M)%M;
}
return (ans%M+M)%M;
}
int main( )
{
int n;
while(~scanf("%d",&n))
{
for(int i=1; i<=n; i++)
{
scanf("%lld%lld",&mod[i],&k[i]);
}
printf("%lld\n",excrt(n));
}
return 0;
}