寒假集训日志(三)——数论
今天听得简直要崩溃。。。没听懂啥。。。
主要内容:
1.欧几里得(稍微懂了点)
2.中国剩余定理( 稍微懂了点)
3.博弈( 看智商的玩意儿)
(一)欧几里得算法(及其扩展算法)
欧几里得定理就是gcd(辗转相除法)的原理(不懂,只会用)。
扩展算法的运用大概就是用来解一个 ax + by = gcd( a, b )的不定方程。
大致证明步骤: 将a 替换为b, 将b 替换为gcd(b, a%b),又gcd(a,b) = gcd( b, a%b),就可以化为一个等式巴拉巴拉的。然后算法实现的花就用递归:
//第一种,之后修改所得的 x , y 就是一组特解,通解的话直接根据系数在特解的基础上加减就好了 void exGcd ( ll a, ll b, ll &x , ll &y){ if( b== 0) { x =1 ; y = 0; return ; } else{ exGcd ( b, a% b, x, y); ll t = x; x = y; y = t - a/b* y; } } //第二种,简短,d最后得到的是 a, b的最大公约数 。 另外,注意这段代码 x, y需要交换位置的部分 void gcd( int a, int b, int &d, int &x, int &y){ if(!b) { d = a ; x =1 ; y = 0 ;} else { gcd( b, a%b, d, y, x); y-= x*(a/b); }
}
今天真正会做的也就3道。。。两道是这个算法的同类题,就放一道把。。。
Description
我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了 一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬 度线总长L米。现在要你求出它们跳了几次以后才会碰面。
Input
Output
Sample Input
1 2 3 4 5
Sample Output
4
我的代码:
//这个题目要注意一下long long
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> typedef long long ll; using namespace std; ll gcd (ll a ,ll b){ return b==0?a: gcd( b, a%b); } void exGcd ( ll a, ll b, ll &x , ll &y){ if( b== 0) { x =1 ; y = 0; return ; } else{ exGcd ( b, a% b, x, y); ll t = x; x = y; y = t - a/b* y; } } int main(){ ll x, y, m, n, L, k, t; cin>>x>>y>>m>>n>>L; ll a = n - m; ll b = L; ll c = x - y; ll d =gcd ( a,b); if( c % d !=0 ){ cout<<"Impossible"<<endl; return 0; } a /=d; b /= d; c /= d; exGcd ( a, b , t, k ); t = c* t%b; if( t < 0 ) t+=b; //printf("%I64d\n", t); cout<< t<<endl; return 0; }
(二) 中国剩余定理:
就是解决一个似乎是叫韩信点兵的问题。(以下转自http://yzmduncan.iteye.com/blog/1323599/)
互质版的很好懂,就是直接一公式就出来了。
MOD M
非互质版的比较麻烦,代码也有点乱,就是采取合并的方法,暂时没弄懂,需好好体会。
中国剩余定理
中国剩余定理是中国古代求解一次同余方程组的方法,是数论中的一个重要定理。
设m1,m2,m3,...,mk是两两互素的正整数,即gcd(mi,mj)=1,i!=j,i,j=1,2,3,...,k.
则同余方程组:
x = a1 (mod n1)
x = a2 (mod n2)
...
x = ak (mod nk)
模[n1,n2,...nk]有唯一解,即在[n1,n2,...,nk]的意义下,存在唯一的x,满足:
x = ai mod [n1,n2,...,nk], i=1,2,3,...,k。
解可以写为这种形式:
x = sigma(ai* mi*mi') mod(N)
其中N=n1*n2*...*nk,mi=N/ni,mi'为mi在模ni乘法下的逆元。
中国剩余定理非互质版
中国剩余定理求解同余方程要求模数两两互质,在非互质的时候其实也可以计算,这里采用的是合并方程的思想。下面是详细推导。
互质版:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef __int64 int64; int64 a[15],b[15]; int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y) { if(b==0) { x=1,y=0; return a; } int64 d = Extend_Euclid(b,a%b,x,y); int64 t = x; x = y; y = t - a/b*y; return d; } //求解模线性方程组x=ai(mod ni) int64 China_Reminder(int len, int64* a, int64* n) { int i; int64 N = 1; int64 result = 0; for(i = 0; i < len; i++) N = N*n[i]; for(i = 0; i < len; i++) { int64 m = N/n[i]; int64 x,y; Extend_Euclid(m,n[i],x,y); x = (x%n[i]+n[i])%n[i]; result = (result + m*a[i]*x%N)%N; } return result; } int main() { int n; while(scanf("%d",&n)!=EOF) { for(int i = 0; i < n; i++) scanf("%I64d %I64d",&a[i],&b[i]); printf("%I64d\n",China_Reminder(n,b,a)); } return 0; }
非互质版:
/** 中国剩余定理(不互质) */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; typedef __int64 int64; int64 Mod; int64 gcd(int64 a, int64 b) { if(b==0) return a; return gcd(b,a%b); } int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y) { if(b==0) { x=1,y=0; return a; } int64 d = Extend_Euclid(b,a%b,x,y); int64 t = x; x = y; y = t - a/b*y; return d; } //a在模n乘法下的逆元,没有则返回-1 int64 inv(int64 a, int64 n) { int64 x,y; int64 t = Extend_Euclid(a,n,x,y); if(t != 1) return -1; return (x%n+n)%n; } //将两个方程合并为一个 bool merge(int64 a1, int64 n1, int64 a2, int64 n2, int64& a3, int64& n3) { int64 d = gcd(n1,n2); int64 c = a2-a1; if(c%d) return false; c = (c%n2+n2)%n2; c /= d; n1 /= d; n2 /= d; c *= inv(n1,n2); c %= n2; c *= n1*d; c += a1; n3 = n1*n2*d; a3 = (c%n3+n3)%n3; return true; } //求模线性方程组x=ai(mod ni),ni可以不互质 int64 China_Reminder2(int len, int64* a, int64* n) { int64 a1=a[0],n1=n[0]; int64 a2,n2; for(int i = 1; i < len; i++) { int64 aa,nn; a2 = a[i],n2=n[i]; if(!merge(a1,n1,a2,n2,aa,nn)) return -1; a1 = aa; n1 = nn; } Mod = n1; return (a1%n1+n1)%n1; } int64 a[1000],b[1000]; int main() { int i; int k; while(scanf("%d",&k)!=EOF) { for(i = 0; i < k; i++) scanf("%I64d %I64d",&a[i],&b[i]); printf("%I64d\n",China_Reminder2(k,b,a)); } return 0; }
题目就不给出了,基本一眼就可以看出来,也很难有什么改变。
(三)博弈论
此类题变幻无穷。。。及其考智商,只是任何时候都别忘了dp。。
另外,打表的方法一定要学会。
直接加上两句话就行了:
freopen("input.txt", "r", stdin); //将文件中的数据输入
freopen("output.txt", "w", stdout); //将程序中的输出输出到文件中