欧几里得算法,扩展欧几里得算法
欧几里得算法(辗转相除法)是用来求两个整数的最大公约数的一种算法
c++实现如下

int gcd(int a,int b){ if(b==0) return a; else return gcd(b,a%b); }
至于为什么是正确的,看下面这两个图就会理解了,一个是gcd的演示动画,另一个是wikipedia上的证明,两条线段的单位长度表示最小公约数,依次,长的线段减去小的线段的长度,直到一条的长度为0,另一条的长度就是所要求的最小公约数;
gcd(欧几里得算法)只能求最大公约数,但是扩展gcd可以做很多的事情;

LL exgcd(LL a,LL b,LL &x,LL &y){ if(b==0){ x=1; y=0; return a; } LL ans=exgcd(b,a%b,x,y); LL temp=x; x=y; y=temp-a/b*y; return ans; }
扩展gcd有很多方面的应用,如求乘法逆元,求解不定方程(a*x+b*y=c),求解同余方程;
首先我们来解释一下exgcd,至少存在一对这样的整数(x,y)使得a*x+b*y=gcd(x,y);(是不是感觉很玄,事实上存在无数多对呢,重点在下面
如果存在a*x+b*y=c,则gcd(a,b) | c,否则不存在整数解,,这一点,额,很显然,额,那就这样吧 (●'◡'●)
a*x+b*y=gcd(a,b) (1)
当gcd结束的时候a为两个数的最小公约数,b=0,对于这个时候的(1)来说存在x=1,y=0满足条件;
根据gcd的递推 a*x+b*y=gcd(a,b)
=gcd(b,a%b)
=b*x1+(a%b)*y1
=b*x1+(a-a/b*b)*y1
=a*y1+b*(x1-a/b*y1) 即x=y1,y=x1-a/b*y1;利用递推式就可以求出一个(1)的特解
POJ 1061 青蛙的约会 http://poj.org/problem?id=1061
唉~~~,青蛙都有gf了,我却没有,好伤心,我的小女友 (;′⌒`)
扩展欧几里得模板题,a+n*k-(b+n*k)=p*l,移项后(m-n)*k+p*l=a-b;
m,n,a,b.l.要满足gcd((m-n),p)=a-b,//(a-b)可能是负值所以要对(a-b)特殊处理一下,
否则不存在,两只小青蛙将永远永远永远不会见面,嘤嘤嘤 ( ▼-▼ )

# include <cstdio> # include <iostream> # include <cstring> # include <algorithm> # include <cmath> using namespace std; typedef long long LL; LL exgcd(LL a,LL b,LL &x,LL &y){ if(b==0) { x=1; y=0; return a; } LL ans=exgcd(b,a%b,x,y); LL temp=x; x=y; y=temp-a/b*y; return ans; } LL cal(LL a,LL b,LL c){ LL x,y; LL gcd=exgcd(a,b,x,y); if(c%gcd!=0) return -1; x*=c/gcd; b/=gcd; if(b<0) b=-b; LL ans=x%b; if(ans<=0) ans+=b; return ans; } int main(){ LL a,b,m,n,l; while(scanf("%lld%lld%lld%lld%lld",&a,&b,&m,&n,&l)!=EOF){ LL ans=cal((n-m),l,(a-b)); if(ans==-1) printf("Impossible\n"); else printf("%lld\n",ans); } return 0; }
HDU 2669 Romantic http://acm.hdu.edu.cn/showproblem.php?pid=2669
好一道狗粮题,(இ௰இ)
gcd(a,b)=1,就可以了,如果找女朋友像这么简单就好了

# include <cstdio> # include <iostream> # include <cstring> # include <algorithm> # include <cmath> using namespace std; typedef long long LL; LL n,m; LL exgcd(LL a,LL b,LL &x,LL &y){ if(b==0) { x=1; y=0; return a; } LL ans=exgcd(b,a%b,x,y); LL temp=x; x=y; y=temp-a/b*y; return ans; } LL cal(LL a,LL b){ LL x,y; LL gcd=exgcd(a,b,x,y); if(1%gcd!=0) return -1; b/=gcd; if(b<0) b=-b; LL ans=x%b; if(ans<=0) ans+=b; return ans; } int main(){ while(scanf("%I64d %I64d",&m,&n)!=EOF){ LL dw=cal(m,n),dd=(1-dw*m)/n; if(dw==-1) printf("sorry\n"); else printf("%I64d %I64d\n",dw,dd); } return 0; }
HDU 1576 A/B http://acm.hdu.edu.cn/showproblem.php?pid=1576
这题很巧,A能被B整除,gcd(9973,b)=1;

# include <cstdio> # include <iostream> # include <cstring> # include <algorithm> using namespace std; typedef long long LL; LL n,b; int t; LL exgcd(LL a,LL b,LL &x,LL &y){ if(b==0){ x=1; y=0; return a; } LL ans=exgcd(b,a%b,x,y); LL temp=x; x=y; y=temp-a/b*y; return ans; } LL cal(LL a,LL b,LL c){ LL x,y; LL ans=exgcd(a,b,x,y); x*=n; return (x%9973+9973)%9973; } int main(){ scanf("%d",&t); while(t--){ scanf("%lld %lld",&n,&b); LL ans=cal(b,9973,1); printf("%lld\n",ans); } return 0; }
SHU 423 密码破译 http://acmoj.shu.edu.cn/problem/423/
这道题需要一些额外的东西,传送门https://zh.wikipedia.org/wiki/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95
所以可以知道 d是e对于N的乘法逆元,N=(q-1)*(p-1);
所以直接用快速幂计算出a的d次方mod(m)就可以了;
m可能会超过long long 的长度,所以乘的时候就要对m取模,题目保证结果小于min(p,q)

# include <bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=100+5; LL c[maxn],n[maxn]; LL e,p,q,d,k; LL m; LL exgcd(LL a,LL b,LL &x,LL &y){ if(b==0) { x=1; y=0; return a; } LL ans=exgcd(b,a%b,x,y); LL temp=x; x=y; y=temp-a/b*y; return ans; } LL cal(LL a,LL b){ LL x,y; LL gcd=exgcd(a,b,x,y); LL ans=x%b; if(ans<0) ans+=b; return ans; } LL qplus(LL a,LL b, LL M){ LL ans=0; while(b){ if(b&1) ans+=a%M; a+=a%M; b>>=1; } return ans; } LL qpow(LL a,LL x,LL M){ LL ans=1; while(x){ if(x&1) ans=qplus(ans,a,M); a=qplus(a,a,M); x>>=1; } return ans%M; } int main(){ int t; scanf("%d",&t); while(t--){ scanf("%lld%lld%lld%lld",&e,&p,&q,&k); LL N=(p-1)*(q-1); LL d=cal(e,N); m=q*p; for(int i=1;i<=k;i++){ scanf("%lld",&c[i]); n[i]=qpow(c[i],d,m); } for(int i=1;i<k;i++) printf("%lld ",n[i]); printf("%lld\n",n[k]); } return 0; }