线性同余方程
用扩展欧几里得算法来求线性同余方程
ax+by=1
则解除的x,y有多组解为
x=x0+k*b
y=y0-k*a 其中x0和y0是解出来的解,k是整数
则最小正整数解为x0%b,y0%a
扩展欧几里得模板
int exgcd(int a,int b,int &x,int &y)//拓展欧几里得求ax+by=d的方程 { if(!b) { x=1,y=0; return a; } int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; }
1.同余方程
ax=1(mod b)转化为ax+by=1,则直接用拓展欧几里得算法即可求x,y,最小x解为x0%b
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e7+10; int prime[N],cnt; bool st[N]; int phi[N]; ll s[N];//记录欧拉函数的前缀和 int exgcd(int a,int b,int &x,int &y)//拓展欧几里得求ax+by=d的方程 { if(!b) { x=1,y=0; return a; } int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } int main() { int a,b; cin>>a>>b; int x,y; exgcd(a,b,x,y);//求x,y cout<<((x%b)+b)%b<<endl;//输出mo出来是正的数 return 0; }
2.青蛙的约会
两个青蛙相距b-a米,而每次能缩小跳m-n米,问至少跳多少次使得两个青蛙碰面
设跳了t次,则跳过的距离为t*(m-n)米,又因为是个圈,则设绕了y圈,若相遇则总的米数为
t*(m-n)=(b-a)+y*l,则直接用扩展欧几里得可得t0,则最小的t为t0%l/d
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll exgcd(ll a,ll b,ll &x,ll &y)//拓展欧几里得求ax+by=d的方程 { if(!b) { x=1,y=0; return a; } ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } int main() { ll a,b,n,m,l; cin>>a>>b>>m>>n>>l; ll x,y; ll d=exgcd(m-n,l,x,y); if((b-a)%d) puts("Impossible");//假如b-a不是公约数 else { x*=(b-a)/d;//将x扩大相应的倍数 ll t=abs(l/d);//将d除过来,让右边为1,才能用t0%b为最小这个定里 cout<<((x%t)+t)%t<<endl;//输出最小结果 } return 0; }
3.最幸运的数字
龟速乘解决乘法爆longlong的情况
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll get_euler(ll c)//获取一个数的欧拉数 { ll res=c; for(ll i=2;i<=c/i;i++) if(c%i==0) { while(c%i==0) c/=i; res=res/i*(i-1);//套欧拉函数的公式 } if(c>1) res=res/c*(c-1); return res; } ll qmul(ll a,ll k,ll p)//龟速乘 { ll res=0; while(k) { if(k&1) res=(res+a)%p; a=(a+a)%p; k>>=1; } return res; } ll qmi(ll a,ll k,ll p)//快速幂 { ll res=1; while(k) { //res=(__int128_t)res*a%p也行 if(k&1) res=qmul(res,a,p);//因为乘法会爆ll,所以用龟速乘 //a=(__int128_t)a*a%p也行 a=qmul(a,a,p);//因为乘法会爆ll,所以用龟速乘 k>>=1; } return res; } int main() { int T=1; ll l; while(cin>>l,l) { int d=1; while(l%(d*2)==0&&d*2<=8) d*=2; ll c=9*l/d;//获取要整除的C ll phi=get_euler(c);//获取他的欧拉数 ll res=1e18; if(c%2==0||c%5==0) res=0;//假如跟10不互质,则没答案 for(ll d=1;d*d<=phi;d++)//枚举欧拉数的所有约数 if(phi%d==0)//假如这个是约数了 { if(qmi(10,d,c)==1) res=min(res,d);//判断符不符合条件 if(qmi(10,phi/d,c)==1) res=min(res,phi/d);//判断符不符合条件 } printf("Case %d: %lld\n",T++,res); } return 0; }
__int128_t解决乘法爆longlong的情况
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll get_euler(ll c) { ll res=c; for(ll i=2;i<=c/i;i++) if(c%i==0) { while(c%i==0) c/=i; res=res/i*(i-1); } if(c>1) res=res/c*(c-1); return res; } ll qmi(ll a,ll k,ll p) { ll res=1; while(k) { if(k&1) res=(__int128_t)res*a%p; a=(__int128_t)a*a%p; k>>=1; } return res; } int main() { int T=1; ll l; while(cin>>l,l) { int d=1; while(l%(d*2)==0&&d*2<=8) d*=2; ll c=9*l/d; ll phi=get_euler(c); ll res=1e18; if(c%2==0||c%5==0) res=0; for(ll d=1;d*d<=phi;d++) if(phi%d==0) { if(qmi(10,d,c)==1) res=min(res,d); if(qmi(10,phi/d,c)==1) res=min(res,phi/d); } printf("Case %d: %lld\n",T++,res); } return 0; }
4.曹冲养猪
信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)
中国剩余定理
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=11; int A[N],B[N]; int n; ll exgcd(ll a,ll b,ll &x,ll &y)//用扩展欧几里得求逆元 { if(!b) { x=1,y=0; return a; } ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } int main() { cin>>n; ll M=1,res=0; for(int i=0;i<n;i++) { scanf("%d%d",&A[i],&B[i]); M*=A[i];//处理所有的乘积 } for(int i=0;i<n;i++) { ll Mi=M/A[i];//获取Mi ll ti,x; exgcd(Mi,A[i],ti,x);//求Mi的逆元ti res+=B[i]*Mi*ti;//图片中有讲答案加上即可 } cout<<((res%M)+M)%M<<endl;//输出正的模余数 return 0; }
矩阵乘法
1.斐波那契前 n 项和
信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)
因为数很大不能直接用公式fn=fn-1+fn-2求,得用矩阵乘法来求
定义一个矩阵Fn=[fn,fn+1]
这题构造一个Fn=【fn,fn+1,sn】的矩阵,是前 fn项和,则A矩阵如下:
然后A的n-1次方用快速幂求出来就行了,logn的时间
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=3; int n,m; void mul(int c[],int a[],int b[][N]) { int temp[N]={0}; for(int i=0;i<N;i++)//矩阵乘法 for(int j=0;j<N;j++) temp[i]=(temp[i]+(ll)a[j]*b[j][i])%m; memcpy(c,temp,sizeof temp); } void mul(int c[][N],int a[][N],int b[][N]) { int temp[N][N]={0}; for(int i=0;i<N;i++)//矩阵乘法 for(int j=0;j<N;j++) for(int k=0;k<N;k++) temp[i][j]=(temp[i][j]+(ll)a[i][k]*b[k][j])%m; memcpy(c,temp,sizeof temp); } int main() { cin>>n>>m; int f1[N]={1,1,1};//f1为1时的矩阵 int a[N][N]={ {0,1,0}, {1,1,1}, {0,0,1} };//a矩阵 n--;//求Fn-1 while(n) { if(n&1) mul(f1,f1,a);//res=res*a mul(a,a,a);//a=a*a; n>>=1; } cout<<f1[2]<<endl;//输出Fn-1的中的f(n-1)+1=fn return 0; }